手把手教你使用PIXI,GSAP实现刹车视觉动效 | 猿创营

1,851 阅读3分钟

实现效果

先说下我们想要实现的效果,一开始图中的粒子在左后方移动,感觉就像是在骑单车时路面的效果。然后我们按住刹车的时候,图中的粒子停止,会有一个回弹的效果。

刹车动效2.gif

前置知识点

PIXI 的基本使用

  • 创建应用
// 创建一个PIXI应用
this.app = new PIXI.Application(params)
// 插入到 DOM
document.querySelector(selector).appendChild(this.app.view)
  • 资源加载
/ 创建一个Loader用来加载资源
this.loader = new PIXI.Loader()
// 添加资源
this.loader.add('btn.png',"images/btn.png");
// 加载资源
this.loader.load()
// 监听加载完成
this.loader.onComplete.add(()=>{
    console.log('complete');
})
  • 将资源添加到场景中
// 通过Sprite生成一个可渲染对象
let btnImage = new PIXI.Sprite(this.loader.resources["btn.png"].texture);
// 把渲染后的图片添加到场景中
this.app.stage.addChild(btnImage)
  • 创建容器
// 创建一个容器,可以统一调整容器内的元素
let actionButton = new PIXI.Container()
// 对容器内的元素进行缩放
actionButton.scale.x = actionButton.scale.y = .6
// 往容器添加元素
let btnImage = new PIXI.Sprite(this.loader.resources["btn.png"].texture);
let btnCircle = new PIXI.Sprite(this.loader.resources["btn_circle.png"].texture);
actionButton.addChild(btnImage)
actionButton.addChild(btnCircle)

代码实现

1.代码初始化

index.html

<html>
<head>
    <title>猿创营</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,minimum-scale=1.0,user-scalable=no">
    <style type="text/css">
        html,
        body {
            margin: 0;
            padding: 0;
        }

        div {
            width: 100%;
        }
    </style>
    <script src="https://pixijs.download/release/pixi.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"></script>
    <script src="./js/brakebanner.js"></script>
    <script>
        window.onload = init;

        function init() {
            let banner = new BrakeBanner("#brakebanner");
        }
    </script>
</head>

<body>
    <div id="brakebanner"></div>
</body>

</html>

brakebanner.js

class BrakeBanner{
    constructor(selector){
        this.app = new PIXI.Application({
                width:window.innerWidth,
                height:window.innerHeight,
                backgroundColor:0x000000,
                resizeTo:window // 跟随窗口调整
        })
        document.querySelector(selector).appendChild(this.app.view)
        this.loader = new PIXI.Loader()
        this.stage = this.app.stage
        // 添加资源
        this.loader.add('btn.png',"images/btn.png");
        this.loader.add('btn_circle.png',"images/btn_circle.png");
        this.loader.add('brake_bike.png',"images/brake_bike.png");
        this.loader.add('brake_handlerbar.png',"images/brake_handlerbar.png");
        this.loader.add('brake_lever.png',"images/brake_lever.png");
        this.loader.add('line.jpg',"images/line.jpg");
        // 加载资源
        this.loader.load()
        // 添加监听器
        this.loader.onComplete.add(()=>{
            console.log('complete');
            this.show()
        })
    }
    show(){}
}
   

2.创建动效按钮

我们先来看下要实现的效果

button.gif

思路:整个按钮的动效由三部分组成,一个按钮图片和两个圆圈图片,一个圆圈在做放大隐藏的动效。所以,可以把这三个图片加入到一个容器中,然后通过pivot调整它们的圆心位置,最后通过gsap对圆圈添加放大隐藏的动效。

代码如下:

show(){
    const actionButton = this.createActionButton()
    // 调整按钮的位置
    actionButton.x = actionButton.y = 400
    // 把按钮添加到场景中
    this.stage.addChild(actionButton)
}

createActionButton(){
    // 创建一个容器,可以统一调整容器内的元素
    let actionButton = new PIXI.Container()
    let btnImage = new PIXI.Sprite(this.loader.resources["btn.png"].texture);
    let btnCircle = new PIXI.Sprite(this.loader.resources["btn_circle.png"].texture);
    let btnCircle2 = new PIXI.Sprite(this.loader.resources["btn_circle.png"].texture);
    actionButton.addChild(btnImage)
    actionButton.addChild(btnCircle)
    actionButton.addChild(btnCircle2)
    // 改变轴心
    btnImage.pivot.x = btnImage.pivot.y = btnImage.width/2
    btnCircle.pivot.x = btnCircle.pivot.y = btnCircle.width/2
    btnCircle2.pivot.x = btnCircle2.pivot.y = btnCircle2.width/2

    // 使用 GSAP 添加动效
    btnCircle.scale.x = btnCircle.scale.y = 0.8
    // 放大动效
    gsap.to(btnCircle.scale,{duration:1, x:1.2, y:1.2, repeat:-1});
    // 透明动效
    gsap.to(btnCircle,{duration:1,alpha:0,repeat:-1})

    return actionButton
}

3.把自行车添加到场景中

show(){
		
    // 创建一个自行车容器
    const bikeContainer = new PIXI.Container()
    const bikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture)
    const bikeHandlerImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture)
    const bikeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture)

    // 把自行车图片添加到容器中
    bikeContainer.addChild(bikeLeverImage)
    bikeContainer.addChild(bikeImage)
    bikeContainer.addChild(bikeHandlerImage)
    // 调整刹车的位置
    bikeLeverImage.pivot.x = bikeLeverImage.pivot.y = 455
    bikeLeverImage.x = 722
    bikeLeverImage.y = 900
    this.stage.addChild(bikeContainer)
    // 调整缩放比例
    bikeContainer.scale.x = bikeContainer.scale.y = 0.3

    const actionButton = this.createActionButton()
    // 调整按钮的位置
    actionButton.x = 722 
    actionButton.y = 900
    bikeContainer.addChild(actionButton)
}

4.实现粒子向左后方移动的效果

思路:

  • 创建一个粒子容器,生成10个粒子,随机分布到容器中
  • 让所有的粒子从上到下移动
  • 对粒子容器进行旋转,实现粒子向左后方移动的效果

代码:

show(){
    ...
    // 创建粒子容器
    const particleContainer = new PIXI.Container()
    this.stage.addChild(particleContainer)

    // 调整圆心和角度
    particleContainer.rotation = 35 * Math.PI /180
    particleContainer.pivot.x = window.innerWidth/2
    particleContainer.pivot.y = window.innerHeight/2
    particleContainer.x = window.innerWidth/2
    particleContainer.y = window.innerHeight/2
    // 创建粒子
    let particles = []
    const colors = [0xf1cf54,0xb5cea8, 0xf1cf54, 0x818181, 0x000000];
    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,6)
        gr.endFill()

        let pItem = {
            sx:Math.random()* window.innerWidth,
            sy:Math.random()* window.innerHeight,
            gr,
        }
        gr.x = pItem.sx
        gr.y = pItem.sy
        particleContainer.addChild(gr)
        particles.push(pItem)
    }
    // 设置初始速度
    let speed = 0
    function loop(){
        speed+=0.5
        // 设置最大速度
        speed = Math.min(speed,20)

        lineImage.y += speed
        if(lineImage.y>= window.innerHeight){
            lineImage.y = -lineImage.height
        }

        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.03
            }

            if(pItem.gr.y>=window.innerHeight){
                pItem.gr.y = 0
            }

        }
    }
    loop()
}

5.添加按钮的交互

show(){
    ...
    // 添加交互
    actionButton.interactive = true
    actionButton.buttonMode = true

    actionButton.on('mousedown',()=>{
            // 对刹车图片进行旋转
            gsap.to(bikeLeverImage,{duration:.6, rotation:Math.PI/180*-30})
            pause()
    })

    actionButton.on('mouseup',()=>{
            gsap.to(bikeLeverImage,{duration:.6, rotation:0})
            start()
    })
    
    function start(){
            speed=0
            gsap.ticker.add(loop)
    }

    function pause(){
            gsap.ticker.remove(loop)
            // 粒子还原
            for(let i=0;i<particles.length;i++){
                    let pItem = particles[i]
                    pItem.gr.scale.x = pItem.gr.scale.y = 1
                    gsap.to(pItem.gr, {duration:.6,x:pItem.sx, y:pItem.sy, ease:'elastic.out'})
            }
    }
    start()
}

源码地址

附上全部代码地址,如果对你有帮助,麻烦给个star哈

总结

说实在的,如果是以前,让我去实现这种动效,我完全没思路,但在看了大帅老猿的视频后,才发现实现这种动效原来也没想象中的那么难。大家如果看了文章之后,对一些原理不是很清楚,欢迎去看视频,视频里对原理会讲的比较细。

最后,在 「公众号」 里搜 大帅老猿,在他这里可以学到很多东西!快来和我一起学习!

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿