圖片碎片化渲染特效實現(xiàn)
共 7671字,需瀏覽 16分鐘
·
2024-07-09 09:10
寫在開頭
最近,小編從玩了兩年多的游戲中退游了??,本來以為會一直就這么玩下去,和隊友們相處很融洽,收獲了很多開心快樂的時光??。可惜,游戲的一波更新......準備要開始收割韭菜了,只能無奈選擇棄坑了。
小編屬于貧民玩家,靠著硬肝與白嫖也將游戲號整得還不錯,這兩天把號給賣了??。玩了兩年多,竟然還能賺一點小錢,很開心??。只是...多少有點舍不得的一起組隊的隊友們,唉。??
記錄一下,希望未來還有重逢一日吧,也希望各位一切安好!??
好,回到正題,本文將分享一個圖片碎片化展示的效果,具體效果如下,請諸君按需食用。
原理
這種特效早在幾年前就已經(jīng)出現(xiàn),屬于老演員了??,它最早是經(jīng)常在輪播圖(banner)上應(yīng)用的,那會追求各種花里胡哨的特效,而現(xiàn)在感覺有點返璞歸真了,簡潔實用就行。
今天咱們來看看它的具體實現(xiàn)原理是如何的,且看圖:
一圖勝千言,不知道聰明的你是否看明白了???
大概原理是:通過容器/圖片大小生成一定數(shù)量的小塊,然后每個小塊背景也使用相同圖片,再使用 background-size 與 background-position 屬性調(diào)整背景圖片的大小與位置,使小塊又合成一整張大圖片,這操作和使用"精靈圖"的操作是一樣的,最后,我們再給每個小塊增加動畫效果,就大功告成。
簡單樸實??,你可以根據(jù)這個原理自個嘗試一下,應(yīng)該能整出來吧。??
具體實現(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;
/* 小塊自動換行排列 */
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>
生成無數(shù)小塊填充:
<script>
document.addEventListener('DOMContentLoaded', () => {
const box = document.getElementById('box');
const { width, height } = box.getBoundingClientRect();
// 定義多少個小塊,由多少行和列決定
const row = 14;
const col = 10;
// 計算小塊的寬高
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>
上面,生成多少個小塊是由人為規(guī)定行(row)與列(col)來決定。可能有的場景想用小塊固定的寬高來決定個數(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è)置背景偏移量,讓小塊的背景顯示對應(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)完成一大步了,是不是沒什么難度!??
小塊樣式整好后,接下來,我們需要來給小塊增加動畫,讓它們動起來,并且是有規(guī)律的動起來。
先來整個簡單的透明度動畫,且看:
<!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';
// 給每個小塊增加不同的延時,讓動畫不同時間執(zhí)行
const delay = i * 100; // 延遲時間為毫秒(ms),注意不要太小了
smallBox.style.animationDelay = `${delay}ms`;
box.appendChild(smallBox);
}
}
}
createSmallBox();
});
</script>
</body>
</html>
嘿嘿??,稍微有點意思了吧?
Em...等等,你發(fā)現(xiàn)沒有?怎么有一些小白條?這可不是小編添加的,小塊的邊框(border)已經(jīng)是注釋了的。??
一開始小編以為是常見的"圖片底部白邊"問題,直接設(shè)置一下 display: block 或者 vertical-align : middle 就能解決,結(jié)果還不是,折騰了很久都沒有搞掉這個小白條。??
最后,竟然通過設(shè)置 will-change 屬性能解決這個問題?我所知道的 will-change 應(yīng)該是應(yīng)用在性能優(yōu)化上,解決動畫流暢度問題上的,想不到這里竟然也能用。
看來得去深度學(xué)習(xí)一下?? will-change 屬性的原理過程才行,這里也推薦倔友寫得一篇文章:傳送門。
解決相鄰背景圖片白條/白邊間隙問題:
<script>
document.addEventListener('DOMContentLoaded', () => {
// ...
function createSmallBox() {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
// ...
smallBox.style.willChange = 'transform';
// 在動畫執(zhí)行后,需要重置will-change
const timer = setTimeout(() => {
smallBox.style.willChange = 'initial';
clearTimeout(timer);
}, 2000);
box.appendChild(smallBox);
}
}
}
createSmallBox();
});
</script>
一定要注意 will-change 不可能被濫用,注意重置回來?
這下女神在動畫執(zhí)行后,也清晰可見了,這是全部小塊拼接組成的圖片。
在上述代碼中,咱們看到,通過 animation-delay 去延遲動畫的執(zhí)行,就能制造一個從上到下的漸變效果。
那么,咱們再改改延遲時間,如:
// const delay = i * 100;
// 改成 ?
const delay = j * 100;
效果:
這...好像有那么點意思吧。。。
但是,這漸變...好像還達不到我們開頭 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);
}
}
其實就是增加小塊的樣式動畫而已,再加點旋轉(zhuǎn),再加點縮放,都整上,整上。??
效果:
是不是稍微高級一點?有那味了???
看到上面旋轉(zhuǎn)所用的"樣式變量"沒有?
--rotateX: rotateX(0);
--rotateY: rotateY(0);
不可能無緣無故突然使用,必然是有深意啦。??
現(xiàn)在效果還不夠炫,咱們將樣式變量利用起來,讓"相鄰兩個小塊旋轉(zhuǎn)相反":
<script>
document.addEventListener('DOMContentLoaded', () => {
// ...
function createSmallBox() {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
// ...
// 相鄰兩個小塊旋轉(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>
效果:
這下對味了。??
總的來說,我們可以通過"延遲"執(zhí)行動畫與改變"旋轉(zhuǎn)"行為,讓小塊們呈現(xiàn)不同的動畫效果,或者你只要有足夠多的設(shè)想,你可以給小塊添加不同的動畫效果,相信也能制造出不錯的整體效果。
更多效果
下面列舉一些通過"延遲"執(zhí)行動畫產(chǎn)生的效果,可以瞧瞧哈。
隨機:
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!??
從中心向四周擴散:
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;
那么,到這里就差不多了?
但還有最后一個問題,那就是圖片的大量使用與加載時長的情況可能會導(dǎo)致效果展示不佳,這里你最好進行一些防范措施,如:
圖片鏈接設(shè)置緩存,讓瀏覽器緩存到內(nèi)存或硬盤中。
通過
JS手動將圖片緩存到內(nèi)存,主要就是創(chuàng)建Image對象。將圖片轉(zhuǎn)成
base64使用。直接將圖片放到代碼本地使用。
...
以上等等吧,反正最好就是要等圖片完整加載后再進行效果展示。
至此,本篇文章就寫完啦,撒花撒花。
