圖片碎片化渲染特效實(shí)現(xiàn)
共 7671字,需瀏覽 16分鐘
·
2024-07-24 08:45
寫(xiě)在開(kāi)頭
最近,小編從玩了兩年多的游戲中退游了??,本來(lái)以為會(huì)一直就這么玩下去,和隊(duì)友們相處很融洽,收獲了很多開(kāi)心快樂(lè)的時(shí)光????上В螒虻囊徊ǜ?.....準(zhǔn)備要開(kāi)始收割韭菜了,只能無(wú)奈選擇棄坑了。
小編屬于貧民玩家,靠著硬肝與白嫖也將游戲號(hào)整得還不錯(cuò),這兩天把號(hào)給賣(mài)了??。玩了兩年多,竟然還能賺一點(diǎn)小錢(qián),很開(kāi)心??。只是...多少有點(diǎn)舍不得的一起組隊(duì)的隊(duì)友們,唉。??
記錄一下,希望未來(lái)還有重逢一日吧,也希望各位一切安好!??
好,回到正題,本文將分享一個(gè)圖片碎片化展示的效果,具體效果如下,請(qǐng)諸君按需食用。
原理
這種特效早在幾年前就已經(jīng)出現(xiàn),屬于老演員了??,它最早是經(jīng)常在輪播圖(banner)上應(yīng)用的,那會(huì)追求各種花里胡哨的特效,而現(xiàn)在感覺(jué)有點(diǎn)返璞歸真了,簡(jiǎn)潔實(shí)用就行。
今天咱們來(lái)看看它的具體實(shí)現(xiàn)原理是如何的,且看圖:
一圖勝千言,不知道聰明的你是否看明白了???
大概原理是:通過(guò)容器/圖片大小生成一定數(shù)量的小塊,然后每個(gè)小塊背景也使用相同圖片,再使用 background-size 與 background-position 屬性調(diào)整背景圖片的大小與位置,使小塊又合成一整張大圖片,這操作和使用"精靈圖"的操作是一樣的,最后,我們?cè)俳o每個(gè)小塊增加動(dòng)畫(huà)效果,就大功告成。
簡(jiǎn)單樸實(shí)??,你可以根據(jù)這個(gè)原理自個(gè)嘗試一下,應(yīng)該能整出來(lái)吧。??
具體實(shí)現(xiàn)
布局與樣式:
<!DOCTYPE html>
<html>
<head>
<style>
body{
width: 100%;
height: 100vh;
padding: 0;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
.box {
width: var(--width);
height: var(--height);
display: flex;
/* 小塊自動(dòng)換行排列 */
flex-wrap: wrap;
justify-content: center;
}
.small-box {
background-image: url('https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99b070fcb1de471d9af4f4d5d3f71909~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1120&h=1680&s=2088096&e=png&b=d098d0');
box-sizing: border-box;
background-repeat: no-repeat;
}
</style>
</head>
<body>
<div id="box" class="box"></div>
</body>
</html>
生成無(wú)數(shù)小塊填充:
<script>
document.addEventListener('DOMContentLoaded', () => {
const box = document.getElementById('box');
const { width, height } = box.getBoundingClientRect();
// 定義多少個(gè)小塊,由多少行和列決定
const row = 14;
const col = 10;
// 計(jì)算小塊的寬高
const smallBoxWidth = width / col;
const smallBoxHeight = height / row;
/** @name 創(chuàng)建小塊 **/
function createSmallBox() {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
const smallBox = document.createElement('div');
smallBox.classList.add('small-box');
smallBox.style.width = smallBoxWidth + 'px';
smallBox.style.height = smallBoxHeight + 'px';
smallBox.style.border = '1px solid red';
// 插入小塊
box.appendChild(smallBox);
}
}
}
createSmallBox();
});
</script>
上面,生成多少個(gè)小塊是由人為規(guī)定行(row)與列(col)來(lái)決定。可能有的場(chǎng)景想用小塊固定的寬高來(lái)決定個(gè)數(shù),這也是可以的,只是需要注意處理一下"邊界"的情況。??
調(diào)整小塊背景圖片的大小與位置:
<script>
document.addEventListener('DOMContentLoaded', () => {
// ...
function createSmallBox() {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
// ...
smallBox.style.border = '1px solid red';
// 設(shè)置背景偏移量,讓小塊的背景顯示對(duì)應(yīng)圖片的位置,和以前那種精靈圖一樣
const offsetX = j * smallBoxWidth * -1;
const offsetY = i * smallBoxHeight * -1;
smallBox.style.backgroundPosition = `${offsetX}px ${offsetY}px`;
smallBox.style.backgroundSize = `${width}px ${height}px`;
box.appendChild(smallBox);
}
}
}
createSmallBox();
});
</script>
女神拼接成功,到這里就已經(jīng)完成一大步了,是不是沒(méi)什么難度!??
小塊樣式整好后,接下來(lái),我們需要來(lái)給小塊增加動(dòng)畫(huà),讓它們動(dòng)起來(lái),并且是有規(guī)律的動(dòng)起來(lái)。
先來(lái)整個(gè)簡(jiǎn)單的透明度動(dòng)畫(huà),且看:
<!DOCTYPE html>
<html>
<head>
<style>
/* ... */
.small-box {
/* ... */
opacity: 0;
animation: smallBoxAnimate 2000ms linear forwards;
}
@keyframes smallBoxAnimate {
0% {
opacity: 0;
}
40% {
opacity: 0;
}
70% {
opacity: 1;
}
100% {
opacity: 1;
}
}
</style>
</head>
<body>
<script>
document.addEventListener('DOMContentLoaded', () => {
// ...
function createSmallBox() {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
// ...
// smallBox.style.border = '1px solid red';
// 給每個(gè)小塊增加不同的延時(shí),讓動(dòng)畫(huà)不同時(shí)間執(zhí)行
const delay = i * 100; // 延遲時(shí)間為毫秒(ms),注意不要太小了
smallBox.style.animationDelay = `${delay}ms`;
box.appendChild(smallBox);
}
}
}
createSmallBox();
});
</script>
</body>
</html>
嘿嘿??,稍微有點(diǎn)意思了吧?
Em...等等,你發(fā)現(xiàn)沒(méi)有?怎么有一些小白條?這可不是小編添加的,小塊的邊框(border)已經(jīng)是注釋了的。??
一開(kāi)始小編以為是常見(jiàn)的"圖片底部白邊"問(wèn)題,直接設(shè)置一下 display: block 或者 vertical-align : middle 就能解決,結(jié)果還不是,折騰了很久都沒(méi)有搞掉這個(gè)小白條。??
最后,竟然通過(guò)設(shè)置 will-change 屬性能解決這個(gè)問(wèn)題?我所知道的 will-change 應(yīng)該是應(yīng)用在性能優(yōu)化上,解決動(dòng)畫(huà)流暢度問(wèn)題上的,想不到這里竟然也能用。
看來(lái)得去深度學(xué)習(xí)一下?? will-change 屬性的原理過(guò)程才行,這里也推薦倔友寫(xiě)得一篇文章:傳送門(mén)。
解決相鄰背景圖片白條/白邊間隙問(wèn)題:
<script>
document.addEventListener('DOMContentLoaded', () => {
// ...
function createSmallBox() {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
// ...
smallBox.style.willChange = 'transform';
// 在動(dòng)畫(huà)執(zhí)行后,需要重置will-change
const timer = setTimeout(() => {
smallBox.style.willChange = 'initial';
clearTimeout(timer);
}, 2000);
box.appendChild(smallBox);
}
}
}
createSmallBox();
});
</script>
一定要注意 will-change 不可能被濫用,注意重置回來(lái)?
這下女神在動(dòng)畫(huà)執(zhí)行后,也清晰可見(jiàn)了,這是全部小塊拼接組成的圖片。
在上述代碼中,咱們看到,通過(guò) animation-delay 去延遲動(dòng)畫(huà)的執(zhí)行,就能制造一個(gè)從上到下的漸變效果。
那么,咱們?cè)俑母难舆t時(shí)間,如:
// const delay = i * 100;
// 改成 ?
const delay = j * 100;
效果:
這...好像有那么點(diǎn)意思吧。。。
但是,這漸變...好像還達(dá)不到我們開(kāi)頭 gif 的碎片化效果吧?
那么,碎片化安排上:
.small-box {
/* ... */
--rotateX: rotateX(0);
--rotateY: rotateY(0);
transform: var(--rotateX) var(--rotateY) scale(0.8);
}
@keyframes smallBoxAnimate {
0% {
opacity: 0;
transform: var(--rotateX) var(--rotateY) scale(0.8);
}
40% {
opacity: 0;
transform: var(--rotateX) var(--rotateY) scale(0.8);
}
70% {
opacity: 1;
transform: rotateX(0) rotateY(0) scale(0.8);
}
100% {
opacity: 1;
transform: rotateX(0) rotateY(0) scale(1);
}
}
其實(shí)就是增加小塊的樣式動(dòng)畫(huà)而已,再加點(diǎn)旋轉(zhuǎn),再加點(diǎn)縮放,都整上,整上。??
效果:
是不是稍微高級(jí)一點(diǎn)?有那味了???
看到上面旋轉(zhuǎn)所用的"樣式變量"沒(méi)有?
--rotateX: rotateX(0);
--rotateY: rotateY(0);
不可能無(wú)緣無(wú)故突然使用,必然是有深意啦。??
現(xiàn)在效果還不夠炫,咱們將樣式變量利用起來(lái),讓"相鄰兩個(gè)小塊旋轉(zhuǎn)相反":
<script>
document.addEventListener('DOMContentLoaded', () => {
// ...
function createSmallBox() {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
// ...
// 相鄰兩個(gè)小塊旋轉(zhuǎn)相反
const contrary = (i + j) % 2 === 0;
smallBox.style.setProperty('--rotateX', `rotateX(${contrary ? -180 : 0}deg)`);
smallBox.style.setProperty('--rotateY', `rotateY(${contrary ? 0 : -180}deg)`);
box.appendChild(smallBox);
}
}
}
createSmallBox();
});
</script>
效果:
這下對(duì)味了。??
總的來(lái)說(shuō),我們可以通過(guò)"延遲"執(zhí)行動(dòng)畫(huà)與改變"旋轉(zhuǎn)"行為,讓小塊們呈現(xiàn)不同的動(dòng)畫(huà)效果,或者你只要有足夠多的設(shè)想,你可以給小塊添加不同的動(dòng)畫(huà)效果,相信也能制造出不錯(cuò)的整體效果。
更多效果
下面列舉一些通過(guò)"延遲"執(zhí)行動(dòng)畫(huà)產(chǎn)生的效果,可以瞧瞧哈。
隨機(jī):
const getRandom = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
const delay = getRandom(0, col + row) * 100;
從左上角到右下角:
const delay = (i + j) * 100;
其他的從"右上角到左下角"或者"左下角到右上角"等等的,只要反向調(diào)整一下變量就行了,就靠你自己悟啦,Come On!??
從中心向四周擴(kuò)散:
const delay = ((Math.abs(col / 2 - j) + Math.abs(row / 2 - i))) * 100;
從四周向中心聚齊:
const delay = (col / 2 - Math.abs(col / 2 - j) + (col / 2 - Math.abs(row / 2 - i))) * 100;
那么,到這里就差不多了?
但還有最后一個(gè)問(wèn)題,那就是圖片的大量使用與加載時(shí)長(zhǎng)的情況可能會(huì)導(dǎo)致效果展示不佳,這里你最好進(jìn)行一些防范措施,如:
圖片鏈接設(shè)置緩存,讓瀏覽器緩存到內(nèi)存或硬盤(pán)中。
通過(guò)
JS手動(dòng)將圖片緩存到內(nèi)存,主要就是創(chuàng)建Image對(duì)象。將圖片轉(zhuǎn)成
base64使用。直接將圖片放到代碼本地使用。
...
以上等等吧,反正最好就是要等圖片完整加載后再進(jìn)行效果展示。
至此,本篇文章就寫(xiě)完啦,撒花撒花。
