Most Common GSAP Mistakes
大部分时候我们使用to()和from()方法,而不是fromTo()方法。这是因为to和from方法更灵活,只要调用这两个方法那么就是要么从当前状态变化到某个特定的值,或者从哪个特定的值变化到当前状态,这两个方法的核心的一个重点,是方法执行时的元素当前状态。但是,有个别的情况,你会发现,这样灵活的方式也会有些问题。
from方法的逻辑问题
首先,我们要先牢牢记住,from这个方法,在执行时,就把当前元素的状态变为目标值。你可以看看下面这个代码案例:
如果我们点击一次,这个元素会开始变化。看着一切正常,元素从消失到逐步显现。
然后现在我们试着重复多次快速点击,你会发现元素不再出现了,因为我们这里用了from方法,那么当我们后面连续多次点击的时候,后面的from方法执行时元素的当前状态就是透明度基本为0或者远小于1的状态(后续点击很快),那么元素就会以这个状态为目标变化,那么元素就不会显示了。
要解决这个问题其实非常简单,我们只要使用fromTo方法就好了。或者我们可以提前先创建好一个动画实例然后使用相应的控制方法(文章后面会介绍)。
第二,要记住当使用from和fromTo方法的时候,他们的immediateRender属性是默认为true,也就是当方法执行的时候会马上执行变化。但是如果你使用的是一个to方法之后再马上使用一个from方法,而且两个方法的目标元素和变化值都是一样的,我们来看看会发生什么:
你本来是希望元素先从x为0变化到x为100,然后再返回到x为0。或者你是希望从0变化到100然后停在100的状态。让我们看看这样的代码会是什么效果:
这个盒子从100变化到100然后再返回到0,为什么会这样?
默认情况下,.to()动画效果会等待播放头(可以理解为之时间线上的时间轴)实际移动才进行渲染(因为在时间为0时渲染是浪费CPU周期的,因为没有任何变化)。但是由于from()具有immediateRender: true属性,所以x立即跳到100!然后在下一个tick上运行.to()缓动(因为它是时间轴中的第一个),并记录当前起始值,即100!因此,它会在0.5秒内将100动画化为100。然后运行.from()缓动,其缓存值为0,作为结束值。
如果您有多个时间线影响同一个元素,则此类情况可能有点棘手。因此,在使用.to()和.from()缓动时,请注意它们的工作方式。它们非常强大,但是责任也就越大。在这里的一个简单解决方案是在.to()缓动上设置immediateRender: true,或在.from()缓动上设置immediateRender: false。
第三种情况就也是类似的,但是跟repeatRefresh和repeats相关。
比如我们有一个这样的需求,我想要一个循环的动画效果,有些文字是渐显,有些文字是渐隐。你可以创建一个时间线实例,然后使用from犯法来让文字渐显,然后用一个to方法让文字渐隐。
没问题,能正常执行!下面这个是一样的效果但是是使用了SplitText插件来让整个过程看起来更美观些:
但是只有在一开始的时候会有一个随机颜色的效果。如果我们想要每次重复的时候都有不同随机效果该如何做呢。这个时候,repeatRefresh就能起作用了。让我们添加repeatRefresh:true试试。
动画效果在第一次的时候是正常的,但是第二次的时候元素们就不再显示了!这是为什么呢?
repeatRefresh会使用动画的结束值作为下一次循环的开始值。在这个案例中,文字在第一次变化之后透明度是0。然后当动画开始执行from方法时,这个文字的动画就是会从0开始变化到0,这样就是导致文字没有再出现的原因。
那我们要做的就是要让元素的透明度每次从0变化到1,所以最简单的方式就是改用fromto方法。
现在这个效果就是我们想要的了。也有其他方式来解决我们上面的问题,比如在使用from方法之前先使用set方法,但是这个案例中使用fromTo感觉会更方便些。
可以使用from和to方法的时候却使用fromTo方法
如果可以,尽可能使用from或者to方法,这两个方法的性能表现会比较好,而且可以使用相对的数据进行设置。所以,除非你必须要使用fromTo方法,不然不要用fromTo方法。
没有通过GSAP设置所有的变化
如果你打算使用GSAP进行动画处理,那么最好包括初始的变化状态都通过GSAP来设置(包括SVG元素),这样会有更好的效果,也能避免出现一些莫名的错误:
准确性 - 浏览器总是以像素为单位报告计算出的值,因此GSAP无法区分你在CSS规则中使用其他单位(如%或vw)。此外,如果计算出的值是matrix()或matrix3d(),在旋转和缩放方面本质上是模棱两可的。0度、360度和720度的矩阵是相同的。scaleX为-1的结果与旋转180度和scaleY为-1的结果相同。有无限多个相同的组合会导致一些额外的问题,但当你使用GSAP设置变换相关的值时,所有东西都以完全准确的方式保存下来。
性能 - GSAP缓存变换相关的值,会让整个动画的性能更好。通过CSS各种复杂的单位进行换算计算出的值再进行解析所有组件,会导致整个动画更耗费性能。
如果你担心会有元素会出现那种突然闪现的无样式的状态,那你可以先把元素隐藏起来,然后再通过JavaScript来显示它。当然也可以先用css进行基础样式的设置,然后再通过GSAP来设置一遍。
没有使用xPercent和yPercent
你知道么,当你想要进行位移的设置时,你其实可以把百分比的变化值和其他单位结合起来使用的。这非常有用,比如我们想要某个元素以元素本身中心为参考点,然后产生一定的偏移的位移,我么就可以写成{xPercent: -50, yPercent: -50, x: 100, y: 300}。
我们经常会看到有不少人会在x和y属性中使用百分比的值,但是这样是会引起混乱的。比如说如果你给x和y设置了-50%,然后你又给xPercent设置了-50,你会发现元素是相当于被设置了xPercent:-100,因为x和xPercent都设置了-50%。
所以,当你想要实用百分比的这种位移变化的时候,就就直接使用xPercent和yPercent就好了。
不停地重复创建动画实例
提前创建tweens和timelines有以下几个优点:
性能 - 不需要在需要时才创建它们,可以提前创建。此外,您需要更少的动画实例。大多数情况下,您永远不会注意到,但这是一个好的做法。
简化逻辑 - 特别是与用户交互事件相关的情况。
自由 - 当事件发生时想要暂停动画吗?可以。当用户执行某些操作时想要反转动画吗?没有问题。如果你是在事件回调函数内去创建动画实例,那么这样会更难进行处理。
当您提前创建动画时,您会希望将它们保持暂停状态,直到需要它们。然后,您可以使用控制方法(如.play()、.pause()、.reverse()、.progress()、.seek()、.restart()和.timeScale())来控制它们的播放状态。
下面是一个例子:
有关提前创建动画的更多信息,请参见animating efficiently文章。
有一个例外是当您需要事物是动态的时候,比如初始值可能会变化。例如,如果您正在在图表中的柱状图高度之间进行动画处理,并且用户可能会快速点击不同的按钮,则每次都创建动画是有意义的,以确保它们从当前状态启动(即使是中间状态)如下面的演示。
如果您正在动态地将动画处理到经常更新的新位置,则可能需要考虑gsap.quickTo()方法。
把动画实例添加到了已经结束的时间线上
有一个常见的错误是像这样的:
你注意到错误了吗?如果你将新的tweens添加到已经完成的timeline中,除非你重新运行timeline,否则它们不会被调用。在这些情况下,你几乎总是应该使用先前创建的动画的控制方法或者创建一个新的动画实例(不使用现有的timeline),按照我们在前面部分介绍的指南进行操作。
不使用遍历来处理交互的添加
如果你想要给一些元素添加上某个相同的交互逻辑,你应该使用遍历的方式。
比如,只给一个按钮添加,就不要使用类似'button'这样的选择器来对元素进行处理。
比如,如果要给每一个按钮都添加,应该像下面这样:
在遍历中你可以利用适合的选择器获取到相应元素内部的元素,比如说:
错误地引入GSAP
人们在模块化环境中使用GSAP时常遇到的一个问题是错误导入GSAP或其插件。大多数情况下,可以通过仔细阅读安装页面的相关部分来避免导入错误。我不会在这篇文章中复制所有细节,但如果您遇到任何类型的导入错误,请务必使用该页面。它甚至有一个非常方便的GSAP安装助手工具,可以生成在大多数环境中使用的正确导入代码。
对同样的属性变化同时使用CSS transition和GSAP
您应该避免在使用GSAP动画的元素上应用CSS transition。这对性能来说非常糟糕,因为浏览器会不断中断动画。例如,假设您将宽度从100px动画到500px。在每个单独的tick(requestAnimationFrame)上,GSAP会设置插值的值,但CSS转换基本上会说“NOPE!我不会让你这样做...我将在____秒内过渡到新值...”,然后开始插值。但是在下一个tick上,GSAP会设置一个新值,CSS转换会中断并重新开始,转到新值。一遍又一遍。
这不仅会给浏览器增加很多压力,还会减缓整个动画的时间。例如,如果GSAP tween的持续时间为1秒,CSS转换也设置为1秒,那么它将在两秒后停止移动!
使用过时的语法
别用Lite/Max
我经常看到人们在加载GSAP 3时仍然使用旧语法。尽管旧语法在技术上仍然有效,但新的现代GSAP 3语法更加简洁和简单。此外,旧语法在GSAP 4中将不受支持(虽然这还远未到来,但编写面向未来的代码仍然是个好主意)。
就像下面这样,直接用gsap来编写:
动画曲线要用字符串写法
用字符串的方式更简单方便,也不需要你额外去引入其他的模块。
duration的设置放到变化数据对象中
将持续时间放在变化对象数据中确实需要更多的输入,但这样做可以使事情更易读和直观。GSAP的默认值和效果非常有帮助,但如果将持续时间作为第二个参数,则无法利用它们。
想要了解更多GSAP3的变化,可以看看这个 GSAP 3 变化指引。
数字值不需要变成字符串
比如你想要变化x值到100像素,你不需要写x:"100px",只要写x:100。非常简单!
只有在你需要传入一些其他单位的数据,比如像10vw这种,或者像transformOrigin: "0px 50px"这样的复合数据,你才需要传入字符串。
gsap方法的对象目标可以直接用选择器字符串
我总会看到有人这样写写gsap的代码:
或者这样用上jQuery:
以上两种方法都可以工作,但是可以通过将选择器字符串作为目标传递来简化它们;GSAP将自动使用.querySelectorAll()获取与所有匹配元素列表。因此,可以将上述代码简写为:
您还可以传递一个复杂的选择器字符串,例如“.box,.card”,它将选择所有的盒子和卡片。或者使用元素数组,只要它们是相同类型的(选择器字符串、变量引用、通用对象等)。