基于WEBGL的文字飞溅特效
今年的梅雨很敬业,已经折腾了一个月,仍然没有要下班的意思,经常是前一秒大太阳,下一秒被浇成落汤鸡,大家管这叫暴力梅。
为了蹭下暴力梅的热度,今天给大家带来一阵文字雨,先来看下效果。
灵感来源于LeoSans中的一个示例,LeoSans是一个开源的文字动画库,作者是Google的创意开发者cmiscm,有兴趣可以在github上搜下。
文字撞击水面后飞溅的效果被业界称为Gooey Effect,翻译过来就是粘液特效,是前端小伙伴用来模拟液体效果的利器,由它也催生出了很多玩法。
看起来花里胡哨,实现起来只需要两个简单的步骤:
- 对效果区域添加模糊滤镜
- 在模糊效果基础上增加对比度
是不是很简单?而且用css或者是svg都可以轻松的实现这样的效果,而我们今天介绍的是第三种方案,也是运行效率最高的一种:用webgl的shader来实现。
我们这次使用pixi.js动画库,相信很多前端小伙伴对这个老牌动画库已经很熟悉了,它的最新版本已经默认采用webgl渲染模式,在现代浏览器上有较高性能提升。
下面介绍程序的几个要点,具体细节可以参考源文件。
创建应用
先来创建一个pixijs应用,并将画布设置充满整个屏幕。
initScene() {
this.app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgrondColor: 0xffffff
})
this.app.view.style.width = '100%'
this.app.view.style.height = '100%'
this.app.view.style.position = 'absolute'
this.el.appendChild(this.app.view)
}
添加滤镜
刚才说过,要实现Gooey效果有两个步骤。我们先对整个画布应用模糊滤镜,然后再应用一个自定义滤镜来增加对比度,pixijs中的滤镜都是对webgl shader的封装。
addFilter() {
const blurFilter = new PIXI.filters.BlurFilter(3) //模糊滤镜
//自定义片段着色器,增加alpha区域对比度
const fs = `
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) {
vec4 color = texture2D(uSampler, vTextureCoord);
if(color.a > 0.6) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
} else {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}
`
const thresholdFilter = new PIXI.Filter(undefined, fs, {})
this.app.stage.filters = [blurFilter, thresholdFilter]
this.app.stage.filterArea = this.app.renderer.screen
}
文字效果
要实现文字撞击水面的飞溅效果,首先要取得汉字的点阵坐标,然后用粒子填充坐标路径,最后再对粒子做简单的物理运动,实现触底碰撞效果。
每个汉字都是背景透明的png图片,我们只要通过canvas API对这些图片进行像素采样,就能获得每个文字的点阵坐标。
getImageData() {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
img.onload = () => {
ctx.drawImage(img, 0, 0, size, size)
const imgData = ctx.getImageData(0, 0, size, size)
for(let i = 0; i < imgData.data.length; i+=4) {
const alpha = imgData.data[i+3]
const index = i / 4
if(alpha > 0) {
this.data.push({
x: index % size,
y: index / size | 0
})
}
}
this.createChar()
}
img.src = src
}
粒子弹性碰撞
每个粒子垂直方向有一个重力加速度,每次触底碰撞的能量损失,使反弹高度降低。
update() {
this.vy += this.gravity
this.x += this.vx
this.y += this.vy
if(this.y > this.bottom) {
this.vy *= this.bounce
this.hit = true
}
}
总结
现在你已经掌握粘性特效的原理,发挥你的想象力尝试做出一些有趣的效果吧!欢迎在留言区分享你的成果。
github源码: