【小程序动画合集】10种小程序动画效果实现方法,文章太长建议收藏!

前言

一提小程序与动画,首先想到的是什么?嗯,微信小程序独创了一套动画玩法,官方支持3种动画方案,分别是 createAnimation 、 this.animate 和 CSS3动画 。

1. createAnimation 与 Animation

创建一个动画实例animation。调用实例的方法来描述动画。最后通过动画实例的export方法导出动画数据传递给组件的animation属性。

var animation = wx.createAnimation({
  transformOrigin: "50% 50%",
  duration: 1000,
  timingFunction: "ease",
  delay: 0
})

// step() 表示一组动画的完成,可以在一组动画中调用任意多个动画方法
// 一组动画中的所有动画会同时开始,一组动画完成后才会进行下一组动画
animation.translate(150, 0).rotate(180).step()
animation.opacity(0).scale(0).step()
this.setData({
  animationData: animation.export()
})

2. 关键帧动画 this.animate 接口

从小程序基础库 2.9.0 开始支持一种更友好的动画创建方式,用于代替旧的 wx.createAnimation 。它具有更好的性能和更可控的接口。在页面或自定义组件中,当需要进行关键帧动画时,可以使用 this.animate 接口。

this.animate(selector, keyframes, duration, callback)

官方给出的例子:

this.animate('#container', [
    { opacity: 1.0, rotate: 0, backgroundColor: '#FF0000' },
    { opacity: 0.5, rotate: 45, backgroundColor: '#00FF00'},
    { opacity: 0.0, rotate: 90, backgroundColor: '#FF0000' },
    ], 5000, function () {
      this.clearAnimation('#container', { opacity: true, rotate: true }, function () {
        console.log("清除了#container上的opacity和rotate属性")
      })
  }.bind(this))

3. css3动画

这是界面动画的常见方式,CSS 动画运行效果良好,甚至在低性能的系统上。渲染引擎会使用跳帧或者其他技术以保证动画表现尽可能的流畅。

利用样式实现小程序动画,用法和css用法相似,定义好指定的动画类名后给元素加上即可。

这是一个模仿心跳的动画:

@keyframes heartBeat {
  0% {
    transform: scale(1);
  }

  14% {
    transform: scale(1.3);
  }

  28% {
    transform: scale(1);
  }

  42% {
    transform: scale(1.3);
  }

  70% {
    transform: scale(1);
  }
}

.heartBeat {
  animation-name: heartBeat;
  animation-duration: 1.3s;
  animation-timing-function: ease-in-out;
}

一、购物车商品曲线飞入动画

主要应用到的技术点:

1、小程序wxss布局,以及数据绑定

2、js二次bezier曲线算法

核心算法,写在app.js里


bezier: function (points, times) {

 // 0、以3个控制点为例,点A,B,C,AB上设置点D,BC上设置点E,DE连线上设置点F,则最终的贝塞尔曲线是点F的坐标轨迹。

 // 1、计算相邻控制点间距。

 // 2、根据完成时间,计算每次执行时D在AB方向上移动的距离,E在BC方向上移动的距离。

 // 3、时间每递增100ms,则D,E在指定方向上发生位移, F在DE上的位移则可通过AD/AB = DF/DE得出。

 // 4、根据DE的正余弦值和DE的值计算出F的坐标。

 // 邻控制AB点间距

 var bezier_points = [];

 var points_D = [];

 var points_E = [];

 const DIST_AB = Math.sqrt(Math.pow(points[1]['x'] - points[0]['x'], 2) + Math.pow(points[1]['y'] - points[0]['y'], 2));

 // 邻控制BC点间距

 const DIST_BC = Math.sqrt(Math.pow(points[2]['x'] - points[1]['x'], 2) + Math.pow(points[2]['y'] - points[1]['y'], 2));

 // D每次在AB方向上移动的距离

 const EACH_MOVE_AD = DIST_AB / times;

 // E每次在BC方向上移动的距离 

 const EACH_MOVE_BE = DIST_BC / times;

 // 点AB的正切

 const TAN_AB = (points[1]['y'] - points[0]['y']) / (points[1]['x'] - points[0]['x']);

 // 点BC的正切

 const TAN_BC = (points[2]['y'] - points[1]['y']) / (points[2]['x'] - points[1]['x']);

 // 点AB的弧度值

 const RADIUS_AB = Math.atan(TAN_AB);

 // 点BC的弧度值

 const RADIUS_BC = Math.atan(TAN_BC);

 // 每次执行

 for (var i = 1; i <= times; i++) {

 // AD的距离

 var dist_AD = EACH_MOVE_AD * i;

 // BE的距离

 var dist_BE = EACH_MOVE_BE * i;

 // D点的坐标

 var point_D = {};

 point_D['x'] = dist_AD * Math.cos(RADIUS_AB) + points[0]['x'];

 point_D['y'] = dist_AD * Math.sin(RADIUS_AB) + points[0]['y'];

 points_D.push(point_D);

 // E点的坐标

 var point_E = {};

 point_E['x'] = dist_BE * Math.cos(RADIUS_BC) + points[1]['x'];

 point_E['y'] = dist_BE * Math.sin(RADIUS_BC) + points[1]['y'];

 points_E.push(point_E);

 // 此时线段DE的正切值

 var tan_DE = (point_E['y'] - point_D['y']) / (point_E['x'] - point_D['x']);

 // tan_DE的弧度值

 var radius_DE = Math.atan(tan_DE);

 // 地市DE的间距

 var dist_DE = Math.sqrt(Math.pow((point_E['x'] - point_D['x']), 2) + Math.pow((point_E['y'] - point_D['y']), 2));

 // 此时DF的距离

 var dist_DF = (dist_AD / DIST_AB) * dist_DE;

 // 此时DF点的坐标

 var point_F = {};

 point_F['x'] = dist_DF * Math.cos(radius_DE) + point_D['x'];

 point_F['y'] = dist_DF * Math.sin(radius_DE) + point_D['y'];

 bezier_points.push(point_F);

 }

 return {

 'bezier_points': bezier_points

 };

 }

注释很详细,算法的原理其实也很简单。 源码也发出来吧,github地址:github.com/xiongchenf/…

调用方法和用法就不占篇幅了,都是基础的东西。 

二、模块移动动画

动画效果:

下图有两组动画,分别为 api 方式(上)与 css3 方式(下)完成的效果,点击move按钮,动画启动。

代码实现

以下分别为 css3 与 api 的核心代码:

css3:

    
复制代码
// scss
    @mixin movePublic($oldLeft,$oldTop,$left,$top) {
        from {
          transform:translate($oldLeft,$oldTop);
        }
        to {
          transform:translate($left,$top);
        }
    }
    
    @mixin blockStyle($color,$name) {
        background: $color;
        animation:$name 2s linear infinite alternate;
    }
    .one {
        @include blockStyle(lightsalmon,onemove);
    }
    
    @keyframes onemove {
        @include movePublic(50rpx,-25rpx,-150rpx,0rpx);
    }
    
    .two {
        @include blockStyle(lightblue,twomove);
    }
    
    @keyframes twomove {
        @include movePublic(0rpx,25rpx,-50rpx,0rpx);
    }
    
    .three {
        @include blockStyle(lightgray,threemove);
    }
    
    @keyframes threemove {
        @include movePublic(0rpx,25rpx,50rpx,0rpx);
    }
    
    .four {
        @include blockStyle(grey,fourmove);
    }
    
    @keyframes fourmove {
        @include movePublic(-50rpx,-25rpx,150rpx,0rpx);
    }
复制代码
// js
    moveFunction(){
        this.setData({
            isMove: true
        })
    }
复制代码

css3 中通过动态改变 class 类名来达到动画的效果,如上代码通过 one 、 two 、 three 、 four 来分别控制移动的距离,通过sass可以避免代码过于冗余的问题。 (纠结如何在小程序中使用 sass 的童鞋请看这里哦: wechat-mina-template )

api:

moveClick(){
        this.move(-75,-12.5,25,'moveOne');
        this.move(-25,12.5, 0,'moveTwo');
        this.move(25, 12.5,0,'moveThree');
        this.move(75, -12.5,-25,'moveFour');
        this.moveFunction(); // 该事件触发css3模块进行移动
    },

    // 模块移动方法
    move: function (w,h,m,ele) {
        let self = this;
        let moveFunc = function () {
        let animation = wx.createAnimation({
            duration: 2000,
            delay: 0,
            timingFunction: "linear",
        });
    
        animation.translate(w, 0).step()
        self.setData({ [ele]: animation.export() })
        let timeout = setTimeout(function () {
            animation.translate(m, h).step();
            self.setData({
                // [ele] 代表需要绑定动画的数组对象
                [ele]: animation.export()
            })
          }.bind(this), 2000)
        }
        moveFunc();
        let interval = setInterval(moveFunc,4000)
    }
复制代码

效果图可见,模块之间都是简单的移动,可以将他们的运动变化写成一个公共的事件,通过向事件传值,来移动到不同的位置。其中的参数 w,h,m,ele 分别表示发散水平方向移动的距离、聚拢时垂直方向、水平方向的距离以及需要修改 animationData 的对象。

通过这种方法产生的动画,无法按照原有轨迹收回,所以在事件之后设置了定时器,定义在执行动画2s之后,执行另一个动画。同时 动画只能执行一次 ,如果需要循环的动效,要在外层包裹一个重复执行的定时器到。

查看源码,发现 api 方式是通过 js 插入并改变内联样式来达到动画效果,下面这张动图可以清晰地看出样式变化。

打印出赋值的 animationData , animates 中存放了动画事件的类型及参数; options 中存放的是此次动画的配置选项, transition 中存放的是 wx.createAnimation 调用时的配置, transformOrigin 是默认配置,意为以对象的中心为起点开始执行动画,也可在 wx.createAnimation时进行配置。

三、音乐播放动画

上面的模块移动动画不涉及逻辑交互,因此新尝试了一个音乐播放动画,该动画需要实现暂停、继续的效果。

动画效果:

两组不同的动画效果对比,分别为 api (上)实现与 css3 实现(下):

代码实现

以下分别是 css3 实现与 api 实现的核心代码:

css3:

    
复制代码
// scss
    .musicRotate{
        animation: rotate 3s linear infinite;
    }
    
    @keyframes rotate{
        from{
            transform: rotate(0deg)
        }
        to{
            transform: rotate(359deg)
        }
    }
    
    .musicPaused{
        animation-play-state: paused;
    }
复制代码
// js
    playTwo(){
        this.setData({
            playTwo: !this.data.playTwo
        },()=>{
            let back = this.data.backgroundAudioManager;
            if(this.data.playTwo){
                back.play();
            } else {
                back.pause();
            }
        })
    }
复制代码

通过 playTwo 这个属性来判断是否暂停,并控制 css 类的添加与删除。当为 false 时,添加 .musicPaused 类,动画暂停。

api:

    
复制代码
// js
    play(){
        this.setData({
            play: !this.data.play
        },()=>{
            let back = this.data.backgroundAudioManager;
            if (!this.data.play) {
                back.pause();
               // 跨事件清除定时器
               clearInterval(this.data.rotateInterval);
            } else {
                back.play();
                // 继续旋转,this.data.i记录了旋转的程度
                this.musicRotate(this.data.i);
            }
        })
    },
    musicRotate(i){
        let self = this;
        let rotateFuc = function(){
            i++;
            self.setData({
                i:i++
            });
            let animation = wx.createAnimation({
                duration: 1000,
                delay: 0,
                timingFunction: "linear",
            });
            animation.rotate(30*(i++)).step()
            self.setData({ musicRotate: animation.export() });
        }
        rotateFuc();
        let rotateInterval = setInterval(
            rotateFuc,1000
        );
        // 全局定时事件
        this.setData({
            rotateInterval: rotateInterval
        })
    }
复制代码

通过 api 实现的方式是通过移除 animationData 来控制动画,同时暂停动画也需要清除定时器,由于清除定时器需要跨事件进行操作,所以定了一个全局方法 rotateInterval 。

api 方式定义了旋转的角度,但旋转到该角度之后便会停止,如果需要实现重复旋转效果,需要通过定时器来完成。因此定义了变量i,定时器每执行一次便加1,相当于每1s旋转30°,对 animation.rotate() 中的度数动态赋值。暂停之后继续动画,需要从原有角度继续旋转,因此变量i需要为全局变量。

代码变化

下图可以看出, api 方式旋转是通过不断累加角度来完成,而非 css3 中循环执行。

四、塔罗牌动画

目前还有个问题:android上面无卡顿,但是ios直接把微信卡掉!

wxml

 
    
  1. { {!start_state?"开始洗牌":"开始选牌"}}

wxss

 
    
  1. .receivenow_view {
  2. display: flex;
  3. flex-direction: column;
  4. justify-content: center;
  5. align-items: center;
  6. padding-bottom: 80rpx;
  7. }
  8. .receivenow_button_view {
  9. font-size: 30rpx;
  10. color: #fff;
  11. padding: 35rpx 190rpx;
  12. border-radius: 60rpx;
  13. background: linear-gradient(to right, #ff5846, #ff067a);
  14. line-height: normal;
  15. }

js

 
    
  1. const animationFrame = require('../../utils/requestAnimationFrame.js')
  2. const ctx0 = wx.createCanvasContext('secondCanvas0')
  3. const ctx = wx.createCanvasContext('secondCanvas1')
  4. Page({
  5. /**
  6. *
  7. */
  8. data: {
  9. //默认canvas高度
  10. height: 375,
  11. //默认canvas宽度
  12. windowWidth: 375,
  13. //背景资源
  14. bg: "",
  15. //卡片资源
  16. card: "",
  17. //是否开始洗牌
  18. start_state: false,
  19. //开场动画是否结束
  20. kaichang: false,
  21. // 是否开始选牌
  22. card_selection: false,
  23. //20张卡开场动画过后的所在位置x坐标
  24. xarrs: [],
  25. //20张卡开场动画过后的所在位置y坐标
  26. yarrs: [],
  27. //开场动画执行时间
  28. start_time: 1500,
  29. card_width: 46,
  30. card_height: 76.3
  31. },
  32. onLoad: function(options) {
  33. var that = this
  34. //获取手机屏幕宽度
  35. wx.getSystemInfo({
  36. success(res) {
  37. let windowWidth = res.windowWidth
  38. let height = windowWidth
  39. that.setData({
  40. height: height,
  41. windowWidth: windowW
  • 11
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值