美文网首页
[源码阅读]解析Anime(JS动画库)核心(2)

[源码阅读]解析Anime(JS动画库)核心(2)

作者: stonehank | 来源:发表于2018-07-20 17:19 被阅读0次

    本次解析将分为2篇文章,当前是第二篇,第一篇在这里

    另外,为了能更好的理解这个库,个人写了一个此库的压缩版,实现了核心的功能(主要也是为了更好理解核心功能),内容更少方便阅读,
    地址在这里


    继续上一篇,先把结构图拉过来:

    // anime主体
    function anime(params){
      
      // 定义instance 也是最终返回值
      let instance = createNewInstance(params);
      
      // 外部API 从当前位置开始执行动画
      instance.play = function() {}
      
      // 配置 startTime 和 engineTime(关键)
       instance.tick = function(t) {}
       
      // 对当前engineTime进行判断,确定动画方案(关键)
      function setInstanceProgress(engineTime) {}
      
      // 计算动画当前位置 并且赋值(关键)
      function setAnimationsProgress(insTime){}
    
      // 直接跳到参数time的时间所在的位置
      instance.seek = function(time) {}
      // 外部API 暂停
      instance.pause = function() {}
      // 外部API 反转
      instance.reverse = function() {}
      // 外部API reset
      instance.reset = function() {}
      // 外部API 重新开始
      instance.restart = function() {}
      /*...*/
      return instance
    }
    
    • setAnimationsProgress(省略了一些配置的定义)

    这个函数接受一个参数,就是当前位置所消耗时间(动画起始点),然后在里面计算出每一个动画目标的位置,并且赋值

    // 计算动画当前位置 并且赋值
    function setAnimationsProgress(insTime) {
      /* ... */
      // 这个while逐个计算当前实例中的每个动画的当前位置(通过时间和算法)
      while (i < animationsLength) {
          /* ... */
        // 消耗的时间占总持续时间的比例 在起点终点之间
        const elapsed = minMaxValue(insTime - tween.start - tween.delay, 0, tween.duration) / tween.duration;
        // 通过算法计算当前进度
        const eased = isNaN(elapsed) ? 1 : tween.easing(elapsed, tween.elasticity);
        /* ... */
        // 遍历每一个到达点执行
        for (let n = 0; n < toNumbersLength; n++) {
          let value;
          const toNumber = tween.to.numbers[n];
          const fromNumber = tween.from.numbers[n];
          if (!tween.isPath) {
            // 计算当前具体位置
            value = fromNumber + (eased * (toNumber - fromNumber));
          } else {
            // 进行SVG path计算
            value = getPathProgress(tween.value, eased * toNumber);
          }
          /* ... */
          numbers.push(value);
        }
             /* ... */
            if (!isNaN(n)) {
              // 组合单位 '135.546'+'px'
              if (!b) {
                progress += n + ' ';
              } else {
                progress += n + b;
              }
            }
        /* ... */
        // 组合结果 'translateX('+'135.546px'+')`
        setTweenProgress[anim.type](animatable.target, anim.property, progress, transforms, animatable.id);
        anim.currentValue = progress;
        i++;
      }
      // 遍历结果,逐个target赋值
      const transformsLength = Object.keys(transforms).length;
      if (transformsLength) {
        for (let id = 0; id < transformsLength; id++) {
          if (!transformString) {
            const t = 'transform';
            // 配置兼容性
            transformString = (getCSSValue(document.body, t) ? t : `-webkit-${t}`);
          }
          // 设置style
          instance.animatables[id].target.style[transformString] = transforms[id].join(' ');
        }
      }
      // 记录当前位置所对应的时间
      instance.currentTime = insTime;
      // 设置进度
      instance.progress = (insTime / instance.duration) * 100;
    }
    

    剩下的就是一些操作函数了:

    • instance.seek
    // 直接跳到参数time的时间所在的位置
    instance.seek = function(time) {
      setInstanceProgress(adjustTime(time));
    }
    
    • instance.pause
    // 外部API 暂停
    instance.pause = function() {
      const i = activeInstances.indexOf(instance);
      // 删除activeInstances 后续engine中找不到便不会执行
      if (i > -1) activeInstances.splice(i, 1);
      instance.paused = true;
    }
    
    • instance.reverse
    // 外部API 反转
    instance.reverse = function() {
      toggleInstanceDirection();
      startTime = 0;
      lastTime = adjustTime(instance.currentTime);
    }
    
    • instance.restart
    // 外部API 重新执行
    instance.restart = function() {
      instance.pause();
      instance.reset();
      instance.play();
    }
    
    • instance.reset
    // 外部API reset
    instance.reset = function() {
      const direction = instance.direction;
      const loops = instance.loop;
      // 当前位置,进度 归零
      instance.currentTime = 0;
      instance.progress = 0;
      instance.paused = true;
      instance.began = false;
      instance.completed = false;
      instance.reversed = direction === 'reverse';
      instance.remaining = direction === 'alternate' && loops === 1 ? 2 : loops;
      setAnimationsProgress(0);
      for (let i = instance.children.length; i--; ){
        instance.children[i].reset();
      }
    }
    

    总结

    1. 使用了requestAnimateFrameCSS动画提高流畅度。
    2. 使用了缓动函数,只需要通过当前动画消耗的时间,搭配其他定义的配置项,就可以计算出当前动画具体位置。

    此次解析就到这里结束,如有错误,请指出,感谢!

    相关文章

      网友评论

          本文标题:[源码阅读]解析Anime(JS动画库)核心(2)

          本文链接:https://www.haomeiwen.com/subject/tnuqmftx.html