乐趣区

关于前端:如何在react中使用pixi快速的做一个的炫酷的刹车特效全注释附源码

思考

因为之前跟着视频学了一下 pixi 写自行车刹车的实战案例,只不过是本来的是 html 原生开发。前面本人也想换个环境进行一下尝试。所以就有了在 react 我的项目中进行的重写。

源码地址

进入 github

步骤

  1. 利用 react 脚手架创立我的项目,react 版本为 17.0.2,将多余的代码删除掉。就能够 run 起来了。而后先在 app.js 引入 PIXI,gsap 这两个包

    import * as PIXI from 'pixi.js'
    import {gsap} from 'gsap'

    而后在此文件中创立一个 id 名为 pixiCavas 的 div 盒子。在页面初始化的时候将画布给 append 进去。当前的步骤都是在 app.js 这个文件中进行操作。

     <div id="pixiCavas">
     </div>

    2. 为了让布局看起来更清新,增加两个全局变量 particles(色彩),speed(速度), 之后的代码都是同一级的

     // 全局变量色彩
      let particles = [];
      // 全局的速度
      let speed = 0

    3. 初始化根 pixi 利用

    const app = new PIXI.Application(
     {
       width: window.innerWidth,
       height: window.innerHeight,
       backgroundColor: 0xFFFFFFFF,
       resizeTo: window
     }
      );

    4. 创立加载器实例,一个 app.loader 只能加载一个图片,所以通过 new PIXI.Loader()创立一个实例来实现同时加载

    const loader = new PIXI.Loader()
    // stage:将图片插入到画布中的办法
    const stage = app.stage

    5. 加载图片,这里引入图片利用了 require。尝试过用 import 通过变量的模式来导入,也是可行的。这里应用 require 看起来更娟秀点

     // 加载图片
      loader.add("btn_img", require('./images/btn.png'))
      loader.add("brake_bike", require('./images/brake_bike.png'))
      loader.add("brake_handlerbar", require('./images/brake_handlerbar.png'))
      loader.add("brake_lever", require('./images/brake_lever.png'))
      loader.add("btn_circle", require('./images/btn_circle.png'))
      // 调用加载器
      loader.load()
      // 加载结束的时候调用 show 办法来开始页面
      loader.onComplete.add(() => {show()
      })

    6. 展现,这里是将自行车相干的代码封装起来的,外面还会调用粒子的成果函数

    const show = () => {
     // 创立刹车
     let bikeLeverImage = new PIXI.Sprite(loader.resources.brake_lever.texture);
     //  创立车架
     let bikeContainer = createBikeContainer(bikeLeverImage)
     stage.addChild(bikeContainer)
     // 创立按钮
     let actionButton = creatActionButton();
     actionButton.x = window.innerWidth - 200
     actionButton.y = 500;
     // 将按钮增加到页面
     stage.addChild(actionButton)
     // 使按钮具备和用户交互的能力
     actionButton.interactive = true
     // 小手成果
     actionButton.buttonMode = true
     // 因为是鼠标事件,这里只对 pc 端失效
     // 鼠标按下
     actionButton.on("mousedown", () => {
       bikeLeverImage.rotation = Math.PI / 180 * -30;
       gsap.to(bikeLeverImage, { duration: .6, rotation: Math.PI / 180 * -30})
       // 鼠标按下粒子静止暂停,speed 归 0
       pause()
       speed = 0
     })
     // 鼠标来到
     actionButton.on("mouseup", () => {
       // 旋转刹车
       gsap.to(bikeLeverImage, { duration: .6, rotation: 0})
       // 鼠标来到粒子持续静止
       start()})
    
     // 创立粒子
     creatParticle()
     // 粒子开始静止
     start()}

    7. 创立车架的函数

    const createBikeContainer = (bikeLeverImage) => {const bikeContainer = new PIXI.Container();
    bikeContainer.scale.x = bikeContainer.scale.y = 0.2
    // 留神: 图片放至的程序会影响图片的层级,先放的图片在底层
    // 自行车架
    let bikeImage = new PIXI.Sprite(loader.resources.brake_bike.texture);
    bikeContainer.addChild(bikeImage)
    // 刹车
    // let bikeLeverImage = new PIXI.Sprite(loader.resources.brake_lever.texture);
    bikeContainer.addChild(bikeLeverImage)
    bikeLeverImage.pivot.x = bikeLeverImage.pivot.y = 455;
    bikeLeverImage.x = 722;
    bikeLeverImage.y = 900;
    // 手把
    let bikeHandleBarImage = new PIXI.Sprite(loader.resources.brake_handlerbar.texture);
    bikeContainer.addChild(bikeHandleBarImage)
    // 监听,让自行车始终呈现在画面右下角
    let resize = () => {
      bikeContainer.x = window.innerWidth - bikeContainer.width
      bikeContainer.y = window.innerHeight - bikeContainer.height
    }
    window.addEventListener('resize', resize)
    resize()
    return bikeContainer
     }

    8. 创立行为按钮

    const creatActionButton = () => {let actionButton = new PIXI.Container()
    //  按住按钮以及两个圈
    let btnImg = new PIXI.Sprite(loader.resources.btn_img.texture)
    let btnCircle = new PIXI.Sprite(loader.resources.btn_circle.texture)
    let btnCircle2 = new PIXI.Sprite(loader.resources.btn_circle.texture)
    
    actionButton.addChild(btnImg)
    actionButton.addChild(btnCircle)
    actionButton.addChild(btnCircle2)
    
    btnImg.pivot.x = btnImg.pivot.y = btnImg.width / 2
    
    btnCircle.pivot.x = btnCircle.pivot.y = btnCircle.width / 2
    btnCircle2.pivot.x = btnCircle2.pivot.y = btnCircle2.width / 2
    
    btnCircle.scale.x = btnCircle.scale.y = 0.8
    // 圆圈 gsap 加载动效
    gsap.to(btnCircle.scale, { duration: 1, x: 1.3, y: 1.3, repeat: -1})
    gsap.to(btnCircle, { duration: 1, alpha: 0, repeat: -1})
    return actionButton
     }

    9. 创立粒子

    const creatParticle = () => {
    // 创立粒子
    let partialContainer = new PIXI.Container()
    stage.addChild(partialContainer)
    // 将粒子盒子旋转 35 度
    partialContainer.rotation = 35 * Math.PI / 180
    // 设置盒子的中心点
    partialContainer.pivot.x = window.innerWidth / 2
    partialContainer.pivot.y = window.innerHeight / 2
    partialContainer.x = window.innerWidth / 2
    partialContainer.y = window.innerHeight / 2
    // 粒子多个色彩
    creatParticleColors(partialContainer)
     }

    10. 粒子色彩创立

    const creatParticleColors = (partialContainer) => {let colors = [0xf1cf54, 0xb5cea8, 0xf1cf54, 0x8182f]
    for (let i = 0; i < 10; i++) {let gr = new PIXI.Graphics();
      gr.beginFill(colors[Math.floor(Math.random() * colors.length)])
      // 绘制小圆点
      gr.drawCircle(0, 0, 10)
      gr.scale.y = 0.4
      gr.scale.x = 0.4
      gr.endFill()
      let pItem = {sx: Math.random() * window.innerWidth,
        sy: Math.random() * window.innerHeight,
        gr: gr
      }
      gr.x = pItem.sx
      gr.y = pItem.sy
      partialContainer.addChild(gr)
      particles.push(pItem)
    }
     }

    11. 小圆点继续挪动,并且在速度大于 20 的时候开始变形,通过扭转粒子的 x,y 的大小来调整为线条和视觉的颗粒感

    const loop = () => {
    speed += .5
    speed = Math.min(speed, 20)
    for (let i = 0; i < particles.length; i++) {let pItem = particles[i]
      pItem.gr.y += speed
      if (speed >= 20) {
        pItem.gr.scale.y = 20
        // 颗粒感
        pItem.gr.scale.x = 0.05
      }
      // 超出边界后从画面外面回来继续移动
      if (pItem.gr.y > window.innerHeight) pItem.gr.y = 0
    }
     }

    12. 小圆点挪动的函数

    const start = () => {loop()
    gsap.ticker.add(loop)
     }

    13. 按住鼠标时革除 loop,并且在 onmouseDown 事件时触发让 speed 从新为 0

    const pause = () => {gsap.ticker.remove(loop)
     for (let i = 0; i < particles.length; i++) {let pItem = particles[i]
     pItem.gr.scale.y = .3
     pItem.gr.scale.x = .3
     // ease: 'elastic.out' 回弹成果
     gsap.to(pItem.gr, { duration: .6, x: pItem.sx, y: pItem.sy, ease: 'elastic.out'})
     }
    }

    14. 最重要的一点,将画布 append 到 div 中, 我是这样写的,有小伙伴有其余好办法能够分享哦

      useEffect(() => {
     // 将利用挂载到 app 的 div 上
     document.querySelector("#pixiCavas").appendChild(app.view)
      }, [])

    感悟

    刚开始的时候想着 npm install pixi 的,发现没有这个包 >>PS: 其实是搞错了,应该这么干: npm install pixi.js。而后应用 pixi 时 @inlet/react-pixi 进行的从新,写到一半快不行了。因为没有找到官网文档,也是参考他人的办法来进行书写,感觉也实现不了成果,头发都快薅光了。前面忽然灵感来了,github 这么大的库应该能够搜到材料,所以就间接找到了 pixi 的官网库进行参考 pixi.js. 当初看起来还好,但当真真切切的本人入手时又是另外的感觉了。用将来的眼光看当初如同也不是什么小事。

    番外

    • 最近也在想开始写文章,通过输入来晋升本人的能力
退出移动版