今年的梅雨很敬业,已经折腾了一个月,仍然没有要下班的意思,经常是前一秒大太阳,下一秒被浇成落汤鸡,大家管这叫暴力梅。

为了蹭下暴力梅的热度,今天给大家带来一阵文字雨,先来看下效果。

灵感来源于LeoSans中的一个示例,LeoSans是一个开源的文字动画库,作者是Google的创意开发者cmiscm,有兴趣可以在github上搜下。

文字撞击水面后飞溅的效果被业界称为Gooey Effect,翻译过来就是粘液特效,是前端小伙伴用来模拟液体效果的利器,由它也催生出了很多玩法。

用来做UI交互特效
用来做大屏互动特效

看起来花里胡哨,实现起来只需要两个简单的步骤:

  1. 对效果区域添加模糊滤镜
  2. 在模糊效果基础上增加对比度

是不是很简单?而且用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源码:

https://github.com/imokya/word-splash