前些日逛掘金,逛到了coco大佬的很炫酷的图片动画:现代 CSS 之高阶图片渐隐消失术 - 掘金 (juejin.cn)
利用 mask
和 @property
做出图片像素化渐隐的效果实在是炫酷,正好现在没项目在摸鱼,脑子一抽开始用 nuxt3
写个人博客,正好首屏展示用得上 (又菜又爱玩)
定义网格
开始的想法是这样的,在大佬的代码基础上修改,通过代码获取到窗口宽高,然后通过 css变量
来动态修改行数列数,试了老半天也不太对,老是报错:
var(--w) is not a number
Undefined operation "calc(var(--w) * var(--h)) + 1"
也许一开始想用 js 去修改预处理器就有问题了🙃
正好又看油管一个大佬的图像碎片化,so,干脆用这个差不多的方法来代替 mask
,做一个丐版图片渐隐,不需要 @property
和 Paint
这种,兼容性还是蛮稳,也就一点点小卡
首先定义一下网格宽高,主要就是一层网格铺在图片前面,颜色跟背景色一致,后面再触发隐藏网格
js
const centerBg = ref(null); // ref 获取到标签
const mask = ref(null); // ref 获取到标签
const row = ref(0); // 行数
const col = ref(0); // 列数
const len = ref(0); // 每个网格的大小
const initImg = () => {
const imageWidth = window.innerWidth;
const imageHeight = window.innerHeight;
const maxGridCount =
window.innerWidth > 1920
? 1000
: window.innerWidth > 1080
? 800
: window.innerWidth > 768
? 600
: 400;
// len 取宽高中较小的值,保证铺满
len.value = Math.min(imageWidth, imageHeight) / Math.sqrt(maxGridCount);
col.value = Math.floor(imageWidth / len.value);
row.value = Math.floor(imageHeight / len.value);
};
const getCellStyle = (rowIndex, colIndex) => {
return {
width: `${len.value}px`,
transition: `opacity ${100 + Math.random() * 500}ms ${
((colIndex + rowIndex / 2) / 20) * Math.random() * 500
}ms`,
};
};
html
<template>
<div
class="centerBg relative w-screen h-screen overflow-hidden flex flex-col justify-center items-center gap-6"
ref="centerBg"
>
<!-- mask -->
<div class="mask absolute inset-0" ref="mask">
<div v-for="rowIndex in row" :key="rowIndex" class="row flex">
<div
v-for="colIndex in col"
:key="colIndex"
class="col flex-1 aspect-square"
:style="getCellStyle(rowIndex, colIndex)"
></div>
</div>
</div>
<!-- title -->
...
<!-- 简介 -->
...
<!-- waves -->
...
</div>
</template>
这段代码也没什么好解释的,len
是取的宽高较小的值为了保证能铺满,然后看下标签那部分,getCellStyle
里面的 transition
随便改,现在是从左往右带一些倾斜
修改 css
先把 css 的部分给写了,后面修改 --o
让网格隐藏就完工~
scss
.centerBg {
--o: 1;
--img: url("assets/images/0.png");
background: var(--img) center center/cover;
.col {
background: var(--bg); // 背景色
opacity: var(--o);
}
}
.dark .centerBg {
--img: url("assets/images/_0.png");
}
触发动画
在完成定义网格和修改样式后,可以用个延时,或者正好可以等图片加载完在触发,用 Image
对象的 decode()
等,不然直接触发是没有效果的
js
onMounted(() => {
initImg();
setTimeout(()=>{
centerBg.value.style.setProperty("--o", 0)
});
});

几百个 div 的动画,卡不卡就另说了 🤔