SVG 滤镜实现图片故障动画
湍流滤镜用到了柏林噪声算法,该算法用于生成随机且自然的纹理,如纸张的褶皱、物体粗糙表面等。
置换滤镜可以让 turbulence 滤镜生成的纹理置换到其他图形或文字上,实现故障效果。
SVG 搭配
前往上方链接,找到 feTurbulence 湍流滤镜和 feDisplacementMap 置换滤镜搭配使用,注意放置的顺序,置换滤镜需要放在最后。配置好后右侧效果图会发生相应的样式变化:

图中左侧部分有很多配置的参数,重要参数说明:
feTurbulencetype置换滤镜的置换类型,可选turbulence湍流和fractalNoise,二者会有噪声的区别。选择turbulence躁点更尖锐,选择fractalNoise躁点更柔和。两张效果图对比:

baseFrequency置换滤镜的频率,频率有两个方向,左侧代表横向,右侧代表纵向。数值越小噪声越小,数值越大噪声越大。噪声越小效果会呈一条直线,噪声越大会有很多躁点。两张效果图对比:

numOctaves置换滤镜的躁点数量,数值越大躁点越多
feDisplacementMapscale置换滤镜的放大倍率,数值越大柏林噪声对其影响越明显,数值越小柏林噪声对其影响越小,如果设为 0 则完全无影响。两张效果图对比:

搭配好之后,效果图下方会生成相应的代码,复制到自己的项目中即可。
SVG 实现
SVG 基本效果
新建一个 .vue 组件,放置一个 img 标签。想要使用前面搭配好的滤镜效果,需要用到 svg 标签包裹着 defs 标签,然后把 copy 的代码放到 defs 标签内。代码如下所示:
<img src="./xxx.jpg" />
<svg style="display: none;">
<defs>
<!-- 复制的代码 -->
</defs>
</svg>然后让 img 使用该效果。由于是 svg 的滤镜效果,因此使用 url ,id 为复制过来 filter 标签的 id,可自行更改。
img {
display: block;
width: 300px;
filter: url(#filter);
}此时刷新查看,能够看到图片出现故障效果,只不过没有动画。
SVG 动画添加
第三方库
实现动画效果,无外乎就是改变 feTurbulence 滤镜的 baseFrequency 参数,修改产生的噪点频率。问题是,如何修改?它不是 CSS 属性,是元素属性,无法用到 transition 过渡。
有一个第三方库 gsap 可以实现这个功能, GSAP(GreenSock Animation Platform)是一个 JavaScript 动画库,提供了丰富的 API 来创建平滑、高性能的动画效果。
安装 GSAP
html<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>shellnpm install gsap基本动画
GSAP 提供了几种基本的动画函数:
gsap.to():最常用的动画类型,从当前状态开始动画到指定状态。gsap.from():与gsap.to()相反,从指定状态动画到当前状态。gsap.fromTo():可以自定义开始和结束状态。gsap.set():立即设置属性,没有动画效果。
时间轴(Timeline)
GSAP 的时间轴功能允许将多个动画组合在一起,并精确控制它们的执行顺序。使用时间轴示例:javascriptconst timeline = gsap.timeline() timeline .to('#box1', { x: 300, duration: 1 }) .to('#box2', { x: 300, duration: 1 }) .to('#box3', { x: 300, duration: 1 })该示例中,三个元素将依次向右移动 300px
控制动画
GSAP 允许在运行时动态控制动画,例如暂停、恢复、反转等:javascriptlet tween = gsap.to('#box', { x: 100, duration: 1 }) // 播放动画 tween.play() // 暂停动画 tween.pause() // 恢复动画 tween.resume() // 反转动画 tween.reverse()这些方法提供了对动画的细粒度控制
代码实现
使用时间线告诉它把什么东西变成什么样子,这里要把频率的数字从 0.4 变为 0.001,持续时间为 0.2,每一次 .to() 就是执行了一次动画。
在 onUpdate 回调函数可以查看该值的变化,把变化后的值赋值给 feTurbulence 标签的 baseFrequency 属性。还能设 paused 为 true 一开始让这个函数暂停,通过 tl.play() 让他启动。动画执行完后还能 tl.restart() 重来。
查看代码
const tb = document.querySelector('feTurbulence')
const tl = new gsap.timeline({
paused: true,
onUpdate() {
console.log(val.value)
tb.setAttribute('baseFrequency', `0 ${val.value}`)
},
})
const val = {
value: 0.4,
}
tl.to(val, {
value: 0.001,
duration: 0.2,
})
tl.to(val, {
value: 0.4,
duration: 0.2,
})
const img = document.querySelector('img')
img.onload = () => {
tl.play()
}
if (img.complete) {
tl.play()
}