缺啥补啥-用PixiJS和GSAP仿写vanmoof动效的入门笔记|猿创营

439 阅读3分钟

前言

写完业务代码,得空也在想再玩点什么,前不久留意到@大帅老猿的猿创营,激起了我这深度潜水党的兴趣。所以跟着大佬学习,来点缺啥补啥~

下面开始学习过程

准备基础环境

直接clone大佬仓库,看看都给我们准备了什么github.com/ezshine/YCY… 项目很简单,引入了PIXIGSAP两个库。一个是优秀的webGL2d渲染引擎,一个是优秀的前端动画引擎。(都没用过,先跟着敲)

<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>

创建PIXI应用

class BrakeBanner {
    constructor(selector) {
        // 初始化PIXI应用,将舞台设置为1920x1080
        this.app = new PIXI.Application({
            width: window.innerWidth,
            height: window.innerHeight,
            backgroundColor: 0xffffff,
            resizeTo: window
        })
        document.querySelector(selector).appendChild(this.app.view);
        this.stage = new PIXI.Container();
        this.stage.sortableChildren = true
        this.app.stage.addChild(this.stage);
    }
}

加载图片资源

    // 创建资源加载器
    this.loader = new PIXI.Loader();
    // 向资源加载器添加资源 key,path
    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.load();
    this.loader.onComplete.add(() => {
        //资源加载完给舞台添加内容
        this.show();
    })

添加按钮(简单的先来)

// 创建按钮容器
const btnContainer = new PIXI.Container();
this.stage.addChild(btnContainer);

// 通过图片创建精灵元素
const btnImage = new PIXI.Sprite(this.loader.resources['btn.png'].texture);
btnContainer.addChild(btnImage);

const btnCircleImage = new PIXI.Sprite(this.loader.resources['btn_circle.png'].texture);
btnContainer.addChild(btnCircleImage)

效果如下

image.png

设置转换中心点

//设置转换中心点
btnImage.pivot.x = btnImage.pivot.y = btnImage.width / 2;
btnCircleImage.pivot.x = btnCircleImage.pivot.y = btnCircleImage.width / 2;

btnContainer.x = 200;
btnContainer.y = 200;

btnCircleImage.scale.x = btnCircleImage.scale.y = .8;

初尝GSAP动效

gsap.to(btnCircleImage, { duration: 1, alpha: 0, repeat: -1 });
gsap.to(btnCircleImage.scale, { duration: 1, x: 1.1, y: 1.1, repeat: -1 })

把按钮相关代码整合到createActionButton 函数中

按钮效果就完成啦,如下

IlliterateGoldenArcticwolf-size_restricted.gif

添加车架和刹车把手

车架

const bikeContainer = new PIXI.Container();
this.stage.addChild(bikeContainer);

// 整体进行一个缩放
bikeContainer.scale.x = bikeContainer.scale.y = .3;
bikeContainer.zIndex = 3

const bikeImage = new PIXI.Sprite(this.loader.resources['brake_bike.png'].texture);
bikeContainer.addChild(bikeImage)

const bikeHanderImage = new PIXI.Sprite(this.loader.resources['brake_handlerbar.png'].texture);
bikeContainer.addChild(bikeHanderImage)

刹车把手

const bikeLeverImage = new PIXI.Sprite(this.loader.resources['brake_lever.png'].texture);
bikeContainer.addChild(bikeLeverImage)

bikeLeverImage.x = 255 + 454;
bikeLeverImage.y = 450 + 462;

bikeLeverImage.pivot.x = 454;
bikeLeverImage.pivot.y = 462;

bikeLeverImage.rotation = 0;

再设置一下按钮容器和自行车容器的层级,把按钮放在顶层

// 设置按钮层级
actionBtn.zIndex = 3

// 设置自行车层级
bikeContainer.zIndex = 2

这样车和按钮就可以啦 image.png

添加按钮交互,实现捏刹车/松刹车

actionBtn.on("mousedown",()=>{
    bikeLeverImage.rotation = Math.PI/180*-35; 
}) 
actionBtn.on("mouseup",()=>{
    bikeLeverImage.rotation = 0;
})

加入GSAP

actionBtn.on("mousedown",()=>{
    gsap.to(bikeLeverImage,{duration:.6,rotation : Math.PI/180*-35});
}) 
actionBtn.on("mouseup",()=>{ 
    gsap.to(bikeLeverImage,{duration:.6,rotation : 0});
})

这会就完成了按钮效果

UnlawfulQuaintDromaeosaur-size_restricted.gif

调整下位置

let resize = () => {
    bikeContainer.x = window.innerWidth - bikeContainer.width;
    bikeContainer.y = window.innerHeight - bikeContainer.height;
}
window.addEventListener('resize', resize);
resize();

速度粒子效果

先看一下需求

  • 首先得要有粒子,也就是小圆点
  • 小圆点的颜色有好几种
  • 向某一个角度一直移动
  • 超出底部边界后回到顶部继续移动
  • 按住鼠标停止
  • 停止的时候还有一点回弹的效果
  • 松开鼠标继续

OK,一条一条来实现

创建粒子容器和对象数组

let particleZoneSize = window.innerWidth;
let particlesContainer = new PIXI.Container();
this.stage.addChild(particlesContainer);
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 parItem = {
        sx: Math.random() * particleZoneSize,
        sy: Math.random() * particleZoneSize,
    }

    gr.x = parItem.sx;
    gr.y = parItem.sy;
    gr.zIndex = 1
    particles.push({...parItem,gr})
    particlesContainer.addChild(gr);
}

粒子随机颜色

const colors = [0xf1cf54, 0xb5cea8, 0xf1cf54, 0x818181, 0x000000];
for(let i=0;i<10;i++){ 
    //... 
    gr.beginFill(colors[Math.floor(Math.random() * colors.length)]); 
    //...

让粒子动起来

首先还是理解需求,想象正在骑自行车。我们速度越来越快直到到达最大速度,接着就是匀速运动。那速度越来越快时,我们观察到的粒子就快产生拖影,它在视觉上是被拉长变细的

let speed = 0
function loop() {
    speed += .5;
    speed = Math.min(speed, 20);
    for (let i = 0; i < particles.length; i++) {
        //拿到粒子item和小圆点实例
        let pItem = particles[i];
        let gr = pItem.gr;

        //让粒子走起来
        gr.y += speed;
        if (speed >= 20) {
            gr.scale.y = 40;
            gr.scale.x = 0.03
        }
        //当粒子移动超出范围时回到顶部
        if (gr.y > innerWidth) gr.y = 0;
    }
}
gsap.ticker.add(loop);

UglyBothCavy-size_restricted.gif

等等,好像角度不太对

particlesContainer.pivot.set(particleZoneSize / 2, particleZoneSize / 2);
particlesContainer.rotation = (35 * Math.PI) / 180;
particlesContainer.x = particleZoneSize / 2;
particlesContainer.y = particleZoneSize / 2;

现在可以了

PoliticalUnconsciousInexpectatumpleco-size_restricted.gif

现在我们要实现开始停止并把它和刹车联系起来

开始粒子运动

function start(){ 
    speed = 0; 
    gsap.ticker.add(loop); 
}

停止粒子运动

function pause() {
    //先移除掉requestAnimationFrame的侦听
    gsap.ticker.remove(loop);
    for (let i = 0; i < particles.length; i++) {
        let pItem = particles[i];
        let gr = pItem.gr;
        //恢复小圆点的拉伸比例
        gr.scale.x = gr.scale.y = 1;
        //恢复小圆点透明度
        gr.alpha = 1;
        //让所有的小圆点使用弹性补间动画回到初始坐标
        gsap.to(gr, {
            duration: 0.6,
            x: pItem.sx,
            y: pItem.sy,
            ease: "elastic.out",
        });
    }
}

关联按钮

actionBtn.on("mousedown", () => {
    bikeLeverImage.rotation = Math.PI / 180 * -35;
    gsap.to(bikeLeverImage, { duration: .6, rotation: Math.PI / 180 * -35 });
    pause()
})
actionBtn.on("mouseup", () => {
    bikeLeverImage.rotation = 0;
    gsap.to(bikeLeverImage, { duration: .6, rotation: 0 });
    start()
})

这就实现完了

最终效果

PessimisticExaltedLamprey-size_restricted.gif

代码地址:github.com/hehuan0327/…

写在最后

好多小伙伴都跟着大帅老师实现啦,赶个晚集~ 第一篇文章写的水了点,就当补的作业了。 还是比较喜欢猿创营的形式的,既能学习很多前端知识还能学到真实项目内容。

感谢大帅老师的讲解,在公众号里搜 大帅老猿,在他这里可以学到很多东西。