基于PixiJS的鼠标粒子动画实现

1. 为什么选择PixiJS

     PixiJS是一个非常快的2D sprite渲染引擎。作为一个Javascript的2D渲染器,PixiJS的目标是提供一个快速的、轻量级而且是兼任所有设备的2D库。它主要使用了WebGL技术,能帮助展示、驱动和管理富有交互性的图形、制作游戏,同时也提供无缝 Canvas 回退。使用JavaScript以及其他HTML5技术,结合PixiJS引擎,可以创建出丰富的交互式图形,跨平台的应用程序和游戏。

2. 实现目标

    在网上找到了一些优秀的案例,比如:

    codepen.io/…

    wow.techbrood.com/…

    Canvas%20mouse%20and%20touch%20events

    在研究了一下这些案例的实现代码之后,觉得基于PixiJS实现一个类似的鼠标交互动画在代码层面应该会更简单一些,主要实现以下两点功能:

  • 鼠标移动时,跟随鼠标路径生成一道粒子束;

  • 鼠标点击时,模拟粒子群由中心向四周发射。

3. 实现过程

(1)创建应用和舞台

     使用Pixi上的Application对象创建一个矩形显示区域, 它会自动生成一个HTML 元素,然后在canvas画布上显示图像。

  const ratio = window.devicePixelRatio

  const app = new Application({
      view: this.$refs.starRef,
      transparent: true,
      antialias: true,
      width: document.documentElement.clientWidth /ratio,
      height: document.documentElement.scrollHeight / ratio,
      resolution: ratio
  })

此处值得注意的是,由于本次示例中是给一个滚动页面整体添加鼠标动画,因此所绘制的画布应当覆盖整个HTML文档,故而height的取值应基于scrollHeight属性。

stage(舞台)是Pixi的Container(容器)对象,该舞台对象将作为根容器来保存开发者希望Pixi显示的东西(精灵),可以通过如下方式访问它。

  app.stage

   (2) 创建精灵

    由于本次动画中所生成的粒子都是一样的,因此使用Pixi的Sprite类,采用加载单个图像文件的形式来创建精灵。

   const star = Sprite.from('static/star.png')
   if (isClick) {
      star.x = mouse.x / window.devicePixelRatio
      star.y = mouse.y / window.devicePixelRatio
      star.vx = (Math.random() - 0.5) * 5
      star.vy = (Math.random() - 0.5) * 5
    } else {
      star.x = (mouse.x + (Math.random() - 0.5) * radius) / window.devicePixelRatio
      star.y = (mouse.y + (Math.random() - 0.5) * radius) / window.devicePixelRatio
      star.vx = (Math.random() * 2) - 1
      star.vy = (Math.random() * 2) - 1
    }
    const scale = 0.7 - Math.random() * 0.3
    star.scale.set(scale, scale)
    star.rotation = Math.random() * Math.PI
    star.alpha = 0.5 + 0.5 * Math.random()
    star.va = Math.random() * 0.1

    stars.push(star)
    app.stage.addChild(star)
  }

    通过x和y属性来定位精灵的位置,引入isClick参数来区分鼠标移动事件和鼠标点击事件,如果是鼠标移动事件,则生成的粒子的初始位置可围绕在鼠标附近一定范围内;如果是鼠标点击事件,则生成的多个粒子的初始位置均为鼠标当前位置。

    通过scale.set方法对精灵进行随机缩放,美化视觉效果。

    通过rotation属性来给精灵设置一个弧度值让它旋转。

   通过alpha属性设置精灵的初始透明度。

   创建完成之后将该精灵通过addChild方法加入到stage,并存入一个stars数组中便于精灵位移刷新显示和增删管理。

  (3) 移动精灵

    在上一步骤中还设置了vx和vy两个速度属性来控制精灵的移动速度,其中vx用于设置精灵在x轴上(水平)的速度和方向, vy用于在y轴上(垂直)设置精灵的速度和方向。 无需直接更改精灵的x和y值,只需通过这两个速度属性更新速度变量,即可实现精灵的移动。而且可以借鉴衍生出va这个消散属性,控制精灵的消失速度。

 function update (star) {
  star.x += star.vx
  star.y += star.vy
  star.alpha -= star.va
 }

(4)动画帧率更新

   使用app.ticker方法实现动画的帧率更新,类似前端的requestAnimationFrame,当浏览器的显示频率刷新的时候,此函数会被执行。

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

 function gameLoop (delta) {
  for (let i = 0; i < stars.length; i++) {
    update(stars[i])
    if (stars[i].opacity <= 0 || stars[i].x < 0 || stars[i].x > app.renderer.width || stars[i].y < 0 || stars[i].y > app.renderer.height) {
      app.stage.removeChild(stars[i])
      stars.slice(i, 1)
    }
  }
 }

    每当浏览器的显示频率刷新, 则遍历存储在stars数组里的各个精灵,更新其位置,并判断其是否已经消失,判定条件满足以下其一即可:

  • 透明度降为0
  • 坐标不在画布内

    若满足消失条件,则先通过removeChild方法在stage中删除该精灵,再在stars数组中删除记录,这一顺序切记不可颠倒。

(5)通过监听鼠标事件添加精灵

   通过在window对象上监听mousemove和click事件来调用生成精灵的函数。应当注意的是,为了更好的动画效果,点击时一次性生成的粒子数目应远多于移动时。

window.addEventListener('mousemove', function (event) {
  mouse.x = event.pageX
  mouse.y = event.pageY
  addSpriteMethods()
})

window.addEventListener('click', function (event) {
  mouse.x = event.pageX
  mouse.y = event.pageY
  addSpriteMethods()
})

(6)适应窗口动态变化

    监听浏览器窗口变化,实现以下两步操作:

  • 重置外部容器的宽高;

  • 使用renderer的resize方法更改画布大小,为了确保画布的大小调整到与分辨率匹配,autoResize需要设置为true。

    window.onresize = () => {
      const SIZE = {
        w: document.documentElement.clientWidth,
        h: document.documentElement.scrollHeight
      }
      this.$refs.starRef.style.width = SIZE.w + 'px'
      this.$refs.starRef.style.height = SIZE.h + 'px'
      app.renderer.resize(SIZE.w / window.devicePixelRatio, SIZE.h / window.devicePixelRatio)
    }

(7)高分屏下的兼容性问题

    在高分屏下,通常会出现图形模糊,因此和canvas解决模糊的思路一样,都需要现将画布进行放大到和实际像素一致,但这一过程中容易造成坐标计算错位的情况,需要格外注意两个要点:

  • 创建应用时设置resolution属性为设备像素比(window.devicePixelRatio);
  • 创建的画布的上下文宽高应是css宽高的1 /window.devicePixelRatio。

(8)提高精灵的实时亮度

    通过滤镜来实现,可以使用PIXI ColorMatrixFilter执行此操作,设置matrix属性,通过矩阵运算提高纹理亮度。但此方法调参难度较大,慎用。

  var colorMatrix =  [
    1,0,0,0,
    0,1,0,0,
    0,0,1,0,
    0,0,0,1
  ]
  var filter = new PIXI.ColorMatrixFilter()
  filter.matrix = colorMatrix
  stage.filters = [filter]

   也可以直接通过使用brightness方法来调节滤镜亮度:

 filter.brightness(num) //num∈[0-1], where 0 is black

4. 小结

    PixiJS的API灵活友好、功能丰富,相比于前端其他的动画制作方法,有利于简化代码,而且具有快速、高效的特点,特别适用于做包含大量元素的粒子动画。本次示例只是一次较为基础的简单实践,可在此思路基础上可以延续拓展出更多动画细节,欢迎多多交流。

原文链接:https://juejin.cn/post/7351321295068299273 作者:37手游前端组

(0)
上一篇 2024年4月1日 下午4:27
下一篇 2024年4月1日 下午4:38

相关推荐

发表回复

登录后才能评论