likes
comments
collection
share

基于PixiJs的下雨动画实现

作者站长头像
站长
· 阅读数 4

1. 安装引入

    在游戏官网或活动脚手架中使用lerna命令为某个项目安装pixi.js依赖:

 lerna add pixi.js --scope=module-name

    然后在项目中按需导入所要调用的类即可,例如:

 import { Application, Sprite, Graphics } from 'pixi.js'

2. 实现过程

1.1 创建舞台对象

    使用Application类创建根容器,并把根容器放到画布中显示。由于一般用作背景动画,因此将容器大小设置为与浏览器的文档显示区域等大。

 // html
 <canvas ref="rainCanvas" />

 //js
 RAIN_APP = new Application({
   view: this.$refs.rainCanvas,
   backgroundAlpha: 0.5,
   width: window.innerWidth,
   height: window.innerHeight,
   antialias: true,
   resolution: 1
 })

1.2 设置动画循环

    依赖canvas的动画需要不断循环执行loop函数,把图像绘制在画布上,以此实现动画。在pixi.js中可通过ticker.add方法,让application实例化对象将render函数添加到渲染队列中,此后每次执行loop是都会促发一个render函数的执行,以此实现画布以每秒60帧的速率进行刷新。

 PIXI_APP.ticker.add(delta => gameLoop(delta))

1.3 随机生成雨滴

   使用Sprite类每次批量生成若干个雨水精灵,使用Sprite.from方法加载图片作为纹理,并给每个雨水精灵都设置独特的位置、位移、缩放和旋转属性,最后使用stage.addChild方法将其逐一加入舞台中。

 function createRainArr () {
 const a = angle[0] + myAngle.get() * (angle[1] - angle[0]) / 100 //入场角度
 const s = speed[0] + mySpeed.get() * (speed[1] - speed[0]) / 100 //入场风速
 if (rainArr.length < maxNum) {
   if (Math.random() < drop_chance) { //用随机值模拟降雨概率情况
     for (let i = 0; i < numLevel; i++) {
       let position_X = 0
       const edge = Math.tan((270 - a) * eachAnger) * PIXI_APP.renderer.height
       if (edge >= 0) {
         position_X = Math.random() * (PIXI_APP.renderer.width + edge)
       } else {
         position_X = Math.random() * (PIXI_APP.renderer.width - edge) + edge
       }
       const drop = Sprite.from(require('../images/HardRain.png'))
       drop.x = position_X                   //入场x坐标
       drop.y = 0                             //入场y坐标
       drop.vx = s * Math.cos(a * eachAnger) //水平方向的位移分速度
       drop.vy = -s * Math.sin(a * eachAnger) //垂直方向的位移分速度
       drop.scale.set(0.2, 0.2)
       drop.rotation = -a * eachAnger
       rainArr.push(drop)
       PIXI_APP.stage.addChild(drop)
     }
   }
 }
 }

1.4 雨滴下落轨迹绘制

    每次动画循环时,对已在容器内的雨水精灵的坐标进行重新计算并绘制,并判断雨滴是否已经掉落至页面底部,若是则应当生成水花,使用stage.removeChild方法将该精灵移除舞台。与此同时,每次循环都应准备生成新的水滴下落。

 function gameLoop (delta) {
 for (let i = 0; i < rainArr.length; i++) {
   updatePosition(rainArr[i])
   if (rainArr[i].y > PIXI_APP.renderer.height) {
     if (hasBounce) {
       let n = Math.round(4 + Math.random() * 4)
       while (n--) {
         createBouncesArr(rainArr[i].x, PIXI_APP.renderer.height)
       }
     }
     PIXI_APP.stage.removeChild(rainArr[i])
     rainArr.splice(i, 1)
   }
 }
 createRainArr()
 ...
}

function updatePosition (sprite) {
 sprite.vy += gravity //加上重力影响
 sprite.x += sprite.vx
 sprite.y += sprite.vy
}

1.5 随机生成水花

   使用Graphics类在画布上绘制一个个随机的圆点来模拟水花,使用stage.addChild方法将其逐一加入舞台中,每个水花粒子都有其独特的位置属性和位移属性。

 function createBouncesArr (pos_x, pos_y) {
 const bounce = new Graphics()
 bounce.beginFill(0xFFFFFF)
 bounce.drawCircle(pos_x, pos_y, 0.6 + Math.random() * 2.4)
 bounce.endFill()
 const dist = Math.random() * 7 //溅起距离
 const angle = Math.PI + Math.random() * Math.PI //反弹角度
 bounce.vx = Math.cos(angle) * dist * 0.3 //水平方向的位移分速度
 bounce.vy = Math.sin(angle) * dist * 0.3 //垂直方向的位移分速度
 bouncesArr.push(bounce)
 PIXI_APP.stage.addChild(bounce)
 }

1.6 水花反弹轨迹绘制

    每次动画循环时,对已在容器内的水花粒子的坐标进行重新计算并绘制,并判断该水花是否已经再次掉落至页面底部,若是则应当使用stage.removeChild方法将该粒子移除舞台。

 function gameLoop (delta) {
 ......
 if (hasBounce) {
   for (let i = 0; i < bouncesArr.length; i++) {
     updatePosition(bouncesArr[i])
     if (bouncesArr[i].y >= PIXI_APP.renderer.height) {
       PIXI_APP.stage.removeChild(bouncesArr[i])
       bouncesArr.splice(i, 1)
     }
   }
 }
 }

1.7 页面缩放自适应

    在window.onresize事件中监听窗口变化,一方面使用resize方法重置舞台大小,另一方面重设外层画布的大小,均与文档区域大小保持一致。

 window.onresize = () => {
   this.SIZE = {
       w: document.documentElement.clientWidth,
       h: document.documentElement.clientHeight
   }
   RAIN_APP.resize(this.SIZE.w, this.SIZE.h)
   this.$refs.rainCanvas.style.width = this.SIZE.w + 'px'
   this.$refs.rainCanvas.style.height = this.SIZE.h + 'px'
 }

3. 参数说明

    为了方便其他业务复用,将上述实现代码整合放入了一个Js文件中,以插件的形式可供调用,提供的功能参数如下:

参数项参数描述默认值
speed以数组形式传入最小和最大风速[10, 100]
gravity雨滴垂直方向的下落速度(模拟重力)0.163
maxNum画布上的雨滴数量上限1000
numLevel每次随机生成的雨滴数量10
drop_chance降雨概率,判断每次是否会生成雨滴,可控制雨滴出现的频率0.1
hasBounce雨滴落地是否有回弹效果true

4. 实现效果

    本文中尽可能真实的模拟了下雨过程,主要亮点包括:

  • 雨水的下落方向和速度不是一成不变的,可随机在预设范围内波动;
  • 雨水触底后模拟了水花四溅的效果。

    页面效果如下图:

基于PixiJs的下雨动画实现