LWDW!

Learn the work from doing the work🍺

当我们讨论动画时,我们在讨论什么——transform篇
Posted on 2017-04-30

基本完成了IFE的动画库任务(一),内心是崩溃的。JS补间动画的原理的确很简单,但在实现过程中,才意识到以前用的成熟的动画库,做了多少的工作。

JS动画实现原理

对于多数成熟的动画库,你只需要提供动画的末状态 end 与动画时长 duration,它就能帮你绘制一段缓动动画,其原理很是单纯,大体可以拆成以下几步:

  1. 根据你提供的末状态 end,获得元素对应的初状态 begin
  2. 通过初末状态,获得状态的变化量 change
  3. 定义一个变量来保存动画进行到的时间,如lastTime;一个变量startTime获得动画开始时的时间(new Date())
  4. 获得当前时间currTime(new Date()),的lastTime = currTime - startTime,将 begin,change,lastTime和duration传入缓动函数,计算出当前时间的状态,并进行设置
  5. 如果lastTime大于duration表明这段动画执行完毕,直接将位置定位到end;否则回到步骤4

但就是这简单的5步,在实现的过程中有无数的坑啊......。

PS: 缓动函数的用法,无耻的贴上如何使用Tween.js各类原生动画运动缓动算法,这次的重点不是这个。

如何从传入的属性值获得必要的数据

为了便于使用,传入的属性值是简单的CSS属性,如:

{
  width: "12px",
  height: "12px"
}

为了将其作为末状态,我们需要提取出

  1. 需要改变的属性名
  2. 对应属性值的数值,因为要计算中间值
  3. 对应属性值的单位

我们可以用正则表达式来匹配12px中的px,将匹配结果保存即可得到单位,再将匹配到的px换成'',即可得到数值的字符串形式。这里要对transform系列的属性值做特别处理,由于transform的属性值是rotate(30deg)这种形式,所以无法直接处理,于是参考其他动画库,对于transform的属性值特别传入,如:

{
  width: "12px",
  height: "12px",
  rotateZ: "30deg",
  translateX: "20px"
}

然后对其的单位提取步骤便可以统一。

如何获得初始状态

初始状态的获得主要依靠getComputedStyle(el, null).getPropertyValue(propertyName)方法,由于要传入属性名,所以肯定要依据添加的动画末状态来找到对应的属性名。这里依然是要特别处理transform系列的属性值,可以用一个正则来匹配末状态传入的属性名,如果是rotateZ这样的,就将transform作为参数传入上面的方法,来获得初始状态的属性...................?结果是返回了matrix(a,b,c,d,e,f)这样的属性值,这其中缘由可见理解CSS3 transform中的Matrix(矩阵),总之我们可以方便的从rotate值计算出matrix值,但是想要反过来(主要是获得rotate值,涉及到三角函数)却很不精确,暂时不考虑这种方法。事实上我们希望能像获取元素的style属性一样,直接得到rotate属性值,但对于设置在CSS样式表里的属性,的确无法直接得到。so目前的一个很坑的地方——对于一个在样式表里设置了transform属性的元素,在第一次进行动画时,无法获得其初状态......这个问题在当前的velocityJs中也仍然存在,会直接从0的值开始动画,这里的解决方法暂时还没有寻得。但至少,之后的动画我们还是有办法掌控的。

如何在计算完成后,设置元素的状态

transform系的没什么好讲的,直接style = 数值 + 单位即可。但是由于transform的属性值很有可能不止一个,如果直接给style赋新值,将会覆盖所有transform属性。解决方法:定义一个transformCache对象,如果有transform属性需要设置,就在transformCache查找是否存在该属性:不存在便定义该属性与对应属性值,如果存在就更新属性值;然后遍历所有的属性,拼接出完整的transform属性。


由于transform有关的很多地方都要特殊处理,所以设计模式的运用得当在该情形下尤为重要,我自己写的似乎就有些混乱了......而且初始状态的transform获取还没有解决,待更新......