• 《微信小程序-进阶篇》Lin-ui组件库源码分析-动画组件Transition(二)


    大家好,这是小程序系列的第十八篇文章,在这一个阶段,我们的目标是 由简单入手,逐渐的可以较为深入的了解组件化开发,从本文开始,将记录分享lin-ui的源码分析,期望通过对lin-ui源码的学习能加深组件化开发的能力:
    1.《微信小程序-进阶篇》package.json必须掌握的字段知识(二)
    2.《微信小程序-进阶篇》Lin-ui组件库源码分析-Icon组件
    3.《微信小程序-进阶篇》Lin-ui组件库源码分析-动画组件Transition(一)
    ——
    求关注,求收藏,求点赞,这是一个系列文章,建议专栏收藏,肯定分享的都是跟小程序相关的,旨在能提高在小程序中的能力,谢谢~

    《微信小程序-进阶篇》Lin-ui组件库源码分析-动画组件Transition(二)

    前言

    上一篇我们介绍了 Transition动画组件 的一些用法,包括 如何触发动画如何修改动画类型持续时间 等等,既然我们已经了解其具体用法,那么本篇开始将逐步去分析这些效果在组件的 内部是如何实现 的,耐心看完,你一定有所收获~

    阅读对象与难度

    本文难度属于:初中级,通过本文你可以了解到 Lin-ui的Transition组件内部的动画效果,name属性以及duration是如何实现的,本文主要内容参考以下思维导图:

    在这里插入图片描述

    动画的实现方式

    先来看看动画的实现方式吧,毕竟 动画是这个组件的核心,在看源码之前我们不妨猜一下,这个动画是由什么实现的,是 CSS 还是 JavaScript
    我个人猜测,应该是CSS,理由也比较简单:

    • 第一,从性能上讲CSS的性能更好,更优,当然不是说 JavaScript 的动画不好,只是因为 JavaScript 的线程本身就需要处理 JavaScript 的代码,再利用其JS的线程去做动画,确实会有一定的性能影响,当然,这个理由有点勉强…实际上这种影响微乎其微,小程序本身也不会出现特别庞大的应用,就这么点动画的真的能产生用户感知上的影响吗?我个人觉得不会;
    • 第二,这写个动画本身并不复杂,CSS的 animate属性 或者 transition属性 完全可以实现,并且实现也要比JavaScript的实现来的简单,JS的动画如果要实现60帧的流畅动画是一件挺困难的事

    为了验证我们的猜想,我们可以来看源码,找到 transition下的index.less文件,如图所示
    在这里插入图片描述

    图中部分代码如下:

    .l-transition {
      transition-timing-function: ease;
    }
    
    .l-fade-enter-active,
    .l-fade-leave-active {
      transition-property: opacity;
    }
    
    .l-fade-enter,
    .l-fade-leave-to {
      opacity: 0;
    }
    
    .l-fade-up-enter-active,
    .l-fade-up-leave-active,
    .l-fade-down-enter-active,
    .l-fade-down-leave-active,
    .l-fade-left-enter-active,
    .l-fade-left-leave-active,
    .l-fade-right-enter-active,
    .l-fade-right-leave-active {
      transition-property: opacity, transform;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    我们发现,在它的样式文件中确实存在着一些貌似 是动画的CSS类名,虽然我们不确定这些类名的动画是否就是产生实际动画的类名,但是看名字有点相像,比如这个 l-fade-up-enter-active,它的类名中存在 fade-up 这个 name属性l-fade-down-leave-active 这个类名中存在 name属性 中的另外一个值 fade-down

    小结

    看到这里,我们姑且能暂时认为 动画组件Transition的动画是由CSS制作的,并且CSS存放的路径就在动画组件Transition下

    动画类型的实现

    动画类型也就是就是name属性,通过上篇我们知道动画组件Transition是通过 name属性 来设定不同的动画,示例如下

    <l-transition show="{{ show }}" name="fade">
      Oliver尹
    l-transition>
    
    • 1
    • 2
    • 3

    但是,当我们打开源码的时候,并没有直接发现存在 name属性,可以看 transition 下的 index.js文件,代码如下:

    // 过渡动画
    import transition from '../behaviors/transition';
    Component({
      /**
       * 组件的属性列表
       */
      behaviors: [transition(true)],
      externalClasses: [
        'l-class',
        'l-enter-class',
        'l-enter-active-class',
        'l-enter-to-class',
        'l-leave-class',
        'l-leave-active-class',
        'l-leave-to-class'
      ],
    
      /**
       * 组件的方法列表
       */
      methods: {
    
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    代码简单有些出乎意料,我们不仅没看到 name属性,甚至连 show 这些属性也没有看到,但意外的是存在两行如下的代码

    // 过渡动画
    import classes from '../behaviors/transition';
    Component({
      /**
       * 组件的属性列表
       */
      behaviors: [transition(true)],
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这是一个 混入,是不是作者把所有的动画都放入到混入文件了,毕竟想想也确实,这几个动画在好多组件中都有可能会被用到,作为一个 公共模块 被管理也理所应当,接着我们打开 …/behaviors/transition 这个文件,确实如我们猜测,在 properties 中看到了 name属性

    properties: {
      name: {
        type: String,
        value: 'fade'
      }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    接着往下看,毕竟 如果是用CSS实现的动画,那么这个CSS 最终必然是需要被加到DOM的class这个属性中去的,因此,我们可以通过 transition组件 的wxml文件来追踪name的实现,但当我们打开这个文件时,代码如下

    <view
        wx:if="{{ inited }}"
        class="l-transition l-class {{ classes }}"
        style="-webkit-transition-duration:{{ currentDuration }}ms; transition-duration:{{ currentDuration }}ms; {{ display ? '' : 'display: none;' }} {{ customStyle }}"
        bind:transitionend="onTransitionEnd"
    >
      <slot />
    view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们并没有看到 name 这个变量属性,只有一个名为 classes的变量,那么是不是classes这个变的结果返回的事一个包含有多个类名的集合呢,返回搜索一下,还好,答案是 肯定的,说明我们的方向没有问题,它里面存在这么一段代码

    const getClassNames = (name) => ({
      enter: `l-${name}-enter l-${name}-enter-active l-enter-class l-enter-active-class`,
      'enter-to': `l-${name}-enter-to l-${name}-enter-active l-enter-to-class l-enter-active-class`,
      leave: `l-${name}-leave l-${name}-leave-active l-leave-class l-leave-active-class`,
      'leave-to': `l-${name}-leave-to l-${name}-leave-active l-leave-to-class l-leave-active-class`
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    位置如下
    在这里插入图片描述

    getClassNames函数返回了一个对象,对象中存在两个key值,分别是enter和leave,

    • enter:存储的是所有执行加载时的动画类名;
    • leave:存储的是所有执行离开时的动画类名;

    很明显,getClassNames 的返回值必然在某个时机添加到classes这个变量上去了,一旦添加上去,那么就会执行类名所赋予的动画效果
    接着,确认一下这个 getClassNames 函数即可,看看其值是否真的会在某个时机将类名添加上去,答案依然肯定是的,代码如下

    methods: {
      enter() {
        const { duration, name } = this.data;
        // 取到类名
        const classNames = getClassNames(name);
        Promise.resolve()
          .then(nextTick)
          .then(() => {
            this.setData({
              inited: true,
              display: true,
              // 赋值加载时的CSS类名
              classes: classNames.enter,
              currentDuration
            });
          })
          .then(nextTick)
          .then(() => {
    
          })
          .catch(() => { });
      },
      leave() {
        if (!this.data.display) {
          return;
        }
        const { duration, name } = this.data;
        const classNames = getClassNames(name);
        Promise.resolve()
          .then(nextTick)
          .then(() => {
            this.setData({
              // 赋值离开时的CSS类名
              classes: classNames.leave,
              currentDuration
            });
          })
      },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    明显的,我们看到类名会在 enterleave 这两个函数中被取到以及赋值给变量classes;

    小结

    到了这时候,我们大概就能猜出来,transtion组件的动画大概率是由CSS实现,Lin-UI会在一开始的时候就 通过name属性,获取到 动画进入时和离开时的类名集合,再将类名集合赋值给DOM,赋值给DOM的一刹那,DOM即会开始执行CSS动画,大致步骤如下图
    在这里插入图片描述

    动画持续时间

    先看一下用法吧,如上一篇中说到的,持续时间在 transtion组件 中的用法如下:

    
    <l-transition show="{{ show }}" name="fade" duration="600">
      Oliver尹
    l-transition>
    
    
    <l-transition show="{{ show }}" name="fade-in" duration="{{ { enter: 300, leave: 1000 } }}">
      Oliver尹
    l-transition>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    那么就需要追踪 duration 这个属性,这个属性在 properties 中确实存在,同样的既然是CSS实现的,那么最终必然是作用到DOM上,

    <view
        style="-webkit-transition-duration:{{ currentDuration }}ms; transition-duration:{{ currentDuration }}ms; {{ display ? '' : 'display: none;' }} {{ customStyle }}"
    >
      <slot />
    view>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    但是我们在wxml文件里没有看到 duration 这个属性的直接使用,反而倒是看到了一个currentDuration 这个变量,以及我们看到了这么两个属性 -webkit-transition-durationtransition-duration ,这两是啥,简单的说就是transition执行动画的时候的持续时间,可以看一下MDN上关于这个的解释

    transition-duration_ 属性以秒或毫秒为单位指定过渡动画所需的时间。默认值为 0s ,表示不出现过渡动画。_
    可以指定多个时长,每个时长会被应用到由 transition-property 指定的对应属性上。如果指定的时长个数小于属性个数,那么时长列表会重复。如果时长列表更长,那么该列表会被裁减。两种情况下,属性列表都保持不变。

    因此,我们可以确认持续时间是由 transition-duration 实现的,而持续时间长则由currentDuration 这个变量决定,因此返回js文件查看 currentDuration变量 ,搜索之后发现在如图的地方使用到了
    在这里插入图片描述

    我们通过上一篇的用法说明知道 duration属性 一共由两种用法,一种是传入的是 可转成数字的字符串,一种是 一个具体到进入和离开时间的对象,在 enter()leave() 这个函数中,它会判断是否是对象

    enter() {
      const { duration, name } = this.data;
      
      // 判断是否是对象,是对象取值duration.enter,否则duration
      const currentDuration = isObj(duration) ? duration.enter : duration;
      
      // 其他代码
    },
    leave() {
        if (!this.data.display) {
          return;
        }
        const { duration, name } = this.data;
        
        // 判断是否是对象,是对象取值duration.leave,否则duration
        const currentDuration = isObj(duration) ? duration.leave : duration;
        
        // 其他代码
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    最终这值会被添加到data中,再在DOM上绑定

    this.setData({
      inited: true,
      display: true,
      classes: classNames.enter,
      // 加入data
      currentDuration
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    小结

    到这里,我们明白,动画的持续时间是靠着CSS的 -webkit-transition-durationtransition-duration 这两个属性实现的,而其对应的值,由于用法上存在两种接收方式,因此在代码中存在一个判断,判断 duration 接收是否是一个对象,如果是对象,那么根据当前动画状态取 enter 或者 leave 的值,如果不是则直接添加,大致步骤如下
    在这里插入图片描述

    小结

    本文显示猜测分析了 动画组件transtion动画实现方式,之后具体看了动画类型 属性name 以及 持续时间duration属性 的实现,我们了解到其实 transition 这个组件的核心都是围绕着CSS的动画实现的,它在开始就已经集合好了要实现的动画类名,之后再添加到DOM上执行动画。
    下一篇将分享动画组件transtion的另外的属性和方法,show以及动画事件;

    (PS:都已经看到这里了,点个赞,求个关注吧,万分感谢~)

  • 相关阅读:
    2020年MathorCup数学建模A题无车承运人平台线路定价问题解题全过程(程序+论文)
    Go入门教程
    SpringBoot 如何实现文件上传和下载
    python 字典里的value
    嵌入式软件开发常用的3种架构
    SpringBoot入门
    android10.0(Q) MTK 6765 user版本打开root权限
    python中的函数(全)
    【Linux】[gdb]Linux环境下如何调试代码
    基于springboot实现校友社交平台管理系统项目【项目源码+论文说明】计算机毕业设计
  • 原文地址:https://blog.csdn.net/zy21131437/article/details/126207647