一个刹车动效带你打开前端动画大门| 猿创营

2,386 阅读7分钟

动画效果在前端知识体系里面属于特殊的存在

有的前端工作基本不需要动画效果,比如 后台管理系统

有的前端工作基本全是动画效果,比如广告H5页面

俗话说的好:技多不压身

多点技能在以后的工作中 总会更有竞争力

下面就是跟着大帅老师学习动画的自身的体验和学习记录

如果您觉得我的文章对您有所帮助,请不吝啬的给个赞,谢谢

先看动画效果

hhh.gif

目标网站是再国外,网速各方面条件可以不是太好,目标网站预览有问题的,直接看我的demo地址

我写完后的预览地址

相关链接整理

目标网页动效的信息提取

这次要模仿的动效是一个自行车刹车的动效,我们先看看从目标网页能得知这个动效用了哪些框架,或者什么有用的信息

网址: www.vanmoof.com/en-NL/s3?co…

  • 通过控制台下面的 页面元素得知 使用的 canvas 我们目前只是知道它用的是canvas,但具体有没有用框架,或者用了什么框架,我们是不得而知,我们这里选用PIXI,当前你也可以选择 其他的你熟悉的
  • 通过 文件内容,可以得知 使用动效框架 gasp 找不到也也没有关系,你也可以选择你所擅长的任何熟悉的动画框架;

框架熟悉

PIXI

  1. 网址:pixijs.com/

  2. 中文文档-别人翻译的

  3. 简介:一个超快的2D渲染引擎

  4. 下面用到的API

    # 1. 创建一个应用 【你可以理解为创建一个 canvas元素】
    let app = new PIXI.Application({具体配置}) 
    # 2.将应用的视图挂载在页面的某个元素 才会展示【你可以理解为将canvas元素绑定到 页面元素上】
    document.querySelector(selector).appendChild(app.view) 
    # 3.资源加载器;可以加载 图片 音视频文件
    let loader = new PIXI.Loader() 
    # 4.挂载资源
    loader.add(imageName,imagePath)
    # 5. 资源加载回调函数
    loader.onComplete.add(()=>{  })
    # 6.创建一个容器【你可以理解为 一个div 父组件,里面可以加载很多字元素】
    let actionButton = new PIXI.Container() 
    # 7.创建精灵【你可以理解为 一个元素】
    let sp new PIXI.Sprite(this.loader.resources['上面资源的key'].texture)  
    # 8.可以将精灵 加到 容器上 【你可以理解为div子元素 添加到父元素上,才能展示】
    actionButton.addChild(sp)
    # 9.设置精灵或者容器的 属性【你可以理解为设置 元素的 宽高 等属性】
    sp.属性 = 值
    # 10. 给精灵添加监听事件
    actionButton.on('mousedown',()=>{}) // 相当于 监听 元素的事件
    

gasp

  1. 网址:greensock.com/docs/v3/GSA…

  2. 简单中文教程

  3. 简介:是一个动画的库

  4. 案例中用到的API

    # 1. to 代表达到某个 动效,可设置相关配置
    gsap.to(btnCircle.scale,{duration:1,x:1.3,y:1.3,repeat:-1}) /
    btnCircle.scale 对元素的 scale 进行动效
    duration:时长
    repeat:-1 无限循环
    # 2.不间断的调用 loop 事件
    gsap.ticker.add(loop) 
    # 3.移除 loop 事件
    gsap.ticker.remove(loop)
    

逐步分解

准备环境的搭建

VSCode 预览插件

使用 Live preview(微软的那一款)

image.png

222.png

image.png

基本环境

基本环境直接使用大帅老师提供的初始环境

点击这里获取地址

教学视频地址

大家可以foke 下来,跟着视频一一来实现

第一步 - 按钮的波纹效果

先看效果

one-good.gif

思路

  • 其实就是一个外面的橙色的环形的圈,把它 从缩小状态 再慢慢的一步一步的 成比例变大

  • 再配合着透明度随着缩放比例变大而便小

具体核心代码

完整代码

    show(){
            // 1.创建 “按住” 的按钮
            let actionButton = this.createActionButton()
            this.stage.addChild(actionButton)
    }
    /**
     * 创建 “按住” 的 按钮
     * 
     */
    createActionButton(){
            // 4. 按住的动效【场景 和 容器的 使用】
            // 容器可以理解为 div 场景只有一个 理解为body
            // pivot 圆心的点
            let actionButton = new PIXI.Container()
            let btnImage = this.helpGetSprite({key:'btn.png'})
            let btnCircle = this.helpGetSprite({key:'btn_circle.png'})
            let btnCircle2 = this.helpGetSprite({key:'btn_circle.png'})
            actionButton.addChild(btnImage)
            actionButton.addChild(btnCircle)
            actionButton.addChild(btnCircle2)
            // 5.修改 坐标
            btnCircle.pivot.x = btnCircle.pivot.y = (btnCircle.width/2)
            btnCircle2.pivot.x = btnCircle2.pivot.y = (btnCircle2.width/2)
            btnImage.pivot.x = btnImage.pivot.y = (btnImage.width/2)

            // to 到达某个动作;duration 时长 x y 变成什么样子 repeat 循环
            btnCircle.scale.x = btnCircle.scale.y = 0.8
            gsap.to(btnCircle.scale,{duration:1,x:1.3,y:1.3,repeat:-1})
            gsap.to(btnCircle,{duration:1,alpha:0,repeat:-1})

            // 配置可以交互
            actionButton.interactive = true
            actionButton.buttonMode = true

            return actionButton
    }

第二步 - 刹车的动效

先看效果

two-good.gif

思路如下

  • 给 “按住”的按钮添加 点击和放开的事件,点击的事件触发
  • 将车把手 以一定的角度 旋转以达到 刹车的效果
  • 当点击事件释放,那么就恢复原样就好了

核心代码如下

完整代码

    show(){
        // 1.创建 “按住” 的按钮
        let actionButton = this.createActionButton()
        // 1.1 添加交互事件:mousedown 鼠标点击事件
        actionButton.on('mousedown',()=>{
                // 按下刹车的动效
                gsap.to(bikeLeverImge,{duration:.6,rotation:Math.PI/180*-30})
        })
        // 1.2 mouseup 鼠标释放
        actionButton.on('mouseup',()=>{
                // 刹车恢复原位的动效
                gsap.to(bikeLeverImge,{duration:.6,rotation:0})
        })
        // 2.创建车身的容器和车身
         let {bikeContainer,bikeLeverImge} = this.createBikeContainer()
        // 注意:addChild 有先后顺序,先addChild 的 图层在下面
        this.stage.addChild(bikeContainer)
        this.stage.addChild(actionButton)
    }
    /**
     * 创建车身和把手的容器
     */
     createBikeContainer(){
            // 自行车整体的容器;你可以理解为一个大的容器
            let bikeContainer = new PIXI.Container()
            bikeContainer.scale.x = bikeContainer.scale.y = 0.3
            // 自行车最下面的轮子+车身的图片=>在图层的最下面,所以先 addChild
            let bikeImge = this.helpGetSprite({key:'brake_bike.png'})
            bikeContainer.addChild(bikeImge)
            // 自行车 按刹车的把柄 
            let bikeLeverImge = this.helpGetSprite({key:'brake_lever.png'})
            bikeContainer.addChild(bikeLeverImge)
            bikeLeverImge.pivot.x = 455;
            bikeLeverImge.pivot.y = 455;
            bikeLeverImge.x = 722;
            bikeLeverImge.y = 900;
            // 自行车的龙头
            let bikeHandlerbarImge = this.helpGetSprite({key:'brake_handlerbar.png'})
            bikeContainer.addChild(bikeHandlerbarImge)
            return {bikeContainer,bikeLeverImge}
    }

第三步 - 粒子效果

先看动效

hhh.gif

完整网页demo预览

思路如下

  • 点击刹车的时候,触动事件

  • 在页面上随机颜色创建几个小球,小球从上到下运动(沿着Y轴),速度越来越快,随着速度变快,变成了一条线

  • 不断的重复这个运动

  • 至于小球的从上到下沿着某个角度下来,只需要将小球的所在容器旋转角度

  • 当松开刹车的时候,小球恢复原样,保持不动

代码如下

完整代码

show(){
    // 1.创建 “按住” 的按钮
    let actionButton = this.createActionButton()
    // 1.1 添加交互事件:mousedown 鼠标点击事件
    actionButton.on('mousedown',()=>{
            // 按下刹车的动效
            gsap.to(bikeLeverImge,{duration:.6,rotation:Math.PI/180*-30})
            // 粒子效果停止
            pause()
    })
    // 1.2 mouseup 鼠标释放
    actionButton.on('mouseup',()=>{
            // 刹车恢复原位的动效
            gsap.to(bikeLeverImge,{duration:.6,rotation:0})
            // 粒子效果开始
            start()
    })
    // 2.创建车身的容器和车身
     let {bikeContainer,bikeLeverImge} = this.createBikeContainer()
    // 注意:addChild 有先后顺序,先addChild 的 图层在下面
    this.stage.addChild(bikeContainer)
    this.stage.addChild(actionButton)
    // 3. 创建粒子
    let {particleContainer,start,pause} = this.createParticleContainer()
    this.stage.addChild(particleContainer)
    // 初始的时候:默认开启粒子效果
    start()
}

/**
 * 创建粒子 和 粒子容器的设置
 */
 createParticleContainer(){
        // 创建粒子的容器
        let particleContainer = new PIXI.Container()
        // NOTE:重点:将容器旋转一定的角度;那么里面的粒子就不需要 计算按照一定的角度 下降,只需要沿着 Y 轴运动即可
        particleContainer.pivot.x = window.innerWidth/2
        particleContainer.pivot.y = window.innerHeight/2
        particleContainer.x = window.innerWidth/2
        particleContainer.y = window.innerHeight/2
        // 旋转容器
        particleContainer.rotation = 35 * Math.PI/180

        let particles = []
        let colors = [0xf1cf54,	0xb5cea8,0xf1cf54,0x818181,0x0000]
        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()
                // 保存每个创建出来的粒子实例 并记录 他初始的 xy 坐标位置:主要是为了 停下来的时候,全部都要复位
                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
        /**
         * 循环函数:
         * 1. 初始速度为了 0 ,每次速度增加 .5;但是最高速度为 20
         * 2. 每次循环 每个粒子的 Y 轴都会 往下移动,移动的量为 当前Y值+当前速度;
         * 3. 每次循环 检查每个粒子 是否超出 window的范围Y值,也就是 页面里看不到了,那么就 y 直接赋值为0;从最上方开始
         * 4. 以上便构成了从慢到快的粒子速度效果
         */
        function 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 = 40
                                pItem.gr.scale.x = .03
                        }
                        if(pItem.gr.y>window.innerHeight) pItem.gr.y = 0
                }
        }
        /**
         * 开始:默认就是开始 动画效果
         * 速度归零
         * 循环事件 开始
         */
        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.y = 1
                        pItem.gr.scale.x = 1
                        // 缓动效果很关键 ease:'elastic.out'
                        gsap.to(pItem.gr,{duration:.6,x:pItem.sx,y:pItem.sy,ease:'elastic.out'})
                }
        }
        return  {particleContainer,start,pause}
}

总结

大帅老师拆解每一步,跟这老师一步一步做,做了一遍,貌似不算难。但其实不是;那是老师教的好 试想一下,如果是你做 即使你思路都对了,说不定一个小小的点,卡住就是 一天,甚至两天都可能!

我在跟着老师敲代码的情况下,都卡住了一会,再对照视频一遍遍看,才解决;事后看,确实是简单的错误,但里面包含了很多的知识点

这个案例我们应该把它看作一个起点,里面还有很多的知识点没有深挖,这仅仅只是开始而已

比如说:我发现中文网站上对这两个框架的 解析 教学 资料 其实是很少的,那么这就是一个机会,可以借此学习 等等

最后推荐下大帅老师,确实很厉害,干货多多;

公众号里搜 大帅老猿,在他这里可以学到很多东西