转载

使用Sass自动化处理CSS动画

有一天, Harry Roberts 有一段有关于他网站上的代码在 twitter 求教,如果有可能,在某些方面得到改善。Harry Roberts做的是使用 keyframes 的carousel动画,所以说使用一些数学计算是有可以得到相应改善。

“Why do we have to learn algebra, Miss? We’re never going to use it…” —Everyone in my maths class bit.ly/UaM2wf

有什么好主意?

据我所见,Harry Roberts在他的 主页 上使用了一个carousel动画。可以使用CSS,为什么要使用JavaScript呢,你说对吧。所以他使用CSS动画来制作Carousel动画。这听起来似乎是一个很好的想法,但直到你需要计算 keyframe 时,你就不会这么认为。

下面是有关于Harry Roberts对Carousel动画做的 描述 。

滚动Carousel(硬编码,有点让人觉得恶心!)和使用一个小动画(这个动画运动是模糊/动画)。整个Carousel动画需要计算过渡和延迟时间(比如 100% ):

n * x + (n - 1) * y = 100 

其中 n 是指幻灯片的数量, x 是动画在静态时的百分比, y 是动画在运动时的百分比。

这个动画有 5 张幻灯片,根据上面描述,可以得到:

5 * x + (5 - 1) * y = 100 

如果知道 xn 的值,我们就可以计算出 y 的值:

(100 - (n * x)) / (n - 1) = y 

假设 x 选择的值为 17.5 (也就是动画中 17.5% 位置),我们知道 n=5 ,这样可以计算出 y=3.125 :

(100 - (5 * 17.5)) / (5 -1) = 3.125 

帧从 17.5% 过渡到 3.125% 等等,直到 100%

如果我们把 x 设置为 15 ,那么可以计算出 y=6.25 :

(100 - (5 * 15)) / (5 -1) = 6.25 

如果 y 运动是从"zero-or-below",这也意味着,我们选择了 x 值太大。

注意,我们还有一个中间点,也就是在中间过渡帧,这个数可以这们得来:

(a * x) + ((a - 1) * y) + (y / 2) 

问题是在 n 个帧哪一个帧才是中间帧。一般情况是 34 之间:

(3 * 17.5) + ((3 - 1) * 3.125)+ (3.125 / 2) = 60.3125 

这的确很混乱。

最终结果是:

@keyframes carousel {   0% {     transform: translate3d(0, 0, 0);     filter: blur(0);   }   17.5% {     transform: translate3d(0, 0, 0);     filter: blur(0);   }   19.0625% {     filter: blur(2px);   }   20.625% {     transform: translate3d(-20%, 0, 0);     filter: blur(0);   }   38.125% {     transform: translate3d(-20%, 0, 0);     filter: blur(0);   }   39.6875% {     filter: blur(2px);   }   41.25%   {     transform: translate3d(-40%, 0, 0);     filter: blur(0);   }   58.75%   {     transform: translate3d(-40%, 0, 0);     filter: blur(0);   }   60.3125% {     filter: blur(2px);   }   61.875%  {     transform: translate3d(-60%, 0, 0);     filter: blur(0);   }   79.375%  {     transform: translate3d(-60%, 0, 0);     filter: blur(0);   }   80.9375% {     filter: blur(2px);   }   82.5%    {     transform: translate3d(-80%, 0, 0);     filter: blur(0);   }   100%     {     transform: translate3d(-80%, 0, 0);     filter: blur(0);   } } 

清洁CSS动画

之前我考虑过Sass,减少动画代码。从上面的代码块中,我们不难发现,有一些关键帧是相同的。这样我们可以把代码清理清理,让整个动画变得更简单:

@keyframes carousel {   0%,   17.5% {     transform: translate3d(0, 0, 0);     filter: blur(0);   }    19.0625% {     filter: blur(2px);   }    20.625%,   38.125% {     transform: translate3d(-20%, 0, 0);     filter: blur(0);   }    39.6875% {     filter: blur(2px);   }    41.25%,   58.75% {     transform: translate3d(-40%, 0, 0);     filter: blur(0);   }    60.3125% {     filter: blur(2px);   }    61.875%,   79.375% {     transform: translate3d(-60%, 0, 0);     filter: blur(0);   }    80.9375% {     filter: blur(2px);   }    82.5%,   100% {     transform: translate3d(-80%, 0, 0);     filter: blur(0);   } } 

Sass怎么玩

关键帧通常可以得到优化。因为 @keyframes 中循环的帧通常很容易产生重复的动画。我们可以试试。

首先,为了保持一致性,我们照常使用Harry的变量名: n , xy 。别忘了它们的含义:

  • $n 是动画中的帧数
  • $x 是动画帧的百分比,逻辑上是小于或等于 100% / $n
  • $y 是动画中每一帧的比例
$n: 5; $x: 17.5%; $y: (100% - $n * $x) / ($n - 1); 

现在我们在 @keyframes 中做一个循环:

@keyframes carousel {     @for $i from 0 to $n { // 0, 1, 2, 3, 4         //Sass动画     } } 

在循环中,我们使用Harry的公式计算每一对相同的关键帧(比如 41.25%58.75% ):

$current-frame: ($i * $x) + ($i * $y); $next-frame: (($i + 1) * $x) + ($i + $y); 

特别声明:小括号完全可以略去,这里只是用它们来让代码保持更干净。

现在,我们使用这些变量来生成关键帧,别忘了在里面插入正确的CSS(有关于更多的Sass内容,可以点击这里):

#{$current-frame, $next-frame} {   transform: translateX($i * -100% / $frames);   filter: blur(0); } 

非常简单,不是吗?运行第一个循环之后,编译出CSS代码:

0%, 17.5% {   transform: translate3d(0%, 0, 0);   filter: blur(0); } 

剩下的就是Harry说的,在中间帧中添加 blur() 效果。我们可以使用他的公式计算出:

$halfway-frame: $i * ($x / 1%) + ($i - 1) * $y + ($y / 2);  #{$halfway-frame} {   filter: blur(2px); } 

这里出错了。

无效的CSS:我们预期的关键帧,比如 10% ,可计算出来的是 -1.5625%

正如你所看到,我们最终得到了一个负的关键帧。这在 CSS规范 中是禁止的,就算在Sass中也认为这是一个错误,所以我们需要确保这种事情不会发生。实际上,只有当 $i0 才会发生。因此,有一个简单的方法来阻止这样的事情发生,根据规则输出 $i 的值:

@if $i > 0 {   #{$halfway-frame} {     filter: blur(2px);   } } 

错误解决了,这是最后的代码:

$n: 5; $x: 17.5%; $y: (100% - $n * $x) / ($n - 1);  @keyframes carousel {   @for $i from 0 to $n {     $current-frame: ($i * $x) + ($i * $y);     $next-frame: (($i + 1) * $x) + ($i + $y);      #{$current-frame, $next-frame} {       transform: translate3d($i * -100% / $frames, 0, 0);     }      $halfway-frame: $i * ($x / 1%) + ($i - 1) * $y + ($y / 2);      @if $i > 0 {       #{$halfway-frame} {         filter: blur(2px);       }     }   } } 

将这一切放在一个mixin中

到目前为止,这一切都好了?Harry的动画代码工作的很好,所以他没有再次计算。如果他想把幻灯片从 5 张变成 4 张,或者说希望动画时间更快或更慢,这一切以得重新开始。

目前我们的变量会污染全范围。同样的,如果在其他地方需要Carousel动画,需要重新设置其他变量名,然后将动画内容复制到新的动画中。这绝不是理想的做法。

这就是我们使用 mixin 的理由。为了让事情更易理解,我们使用实际的变量名来替代这些字母:

  • $n 换成 $frames
  • $x 换成 $static
  • $y 换成 $animating

另外,为了确保它输出不同的动,可以利用 mixin 多次调用不同的参数。所以我们需要再添加一个参数:动画的名字。

@mixin carousel-animation($frames, $static, $name: 'carousel') {   $animating: (100% - $frames * $static) / ($frames - 1);    // Moar Sass } 

由于 mixin 可以在不同的地方调用,为了阻止选择器包括它,可以使用 @at-root 确保动画输出是在根级别:

@mixin carousel-animation($frames, $static, $name: 'carousel') {   $animating: (100% - $frames * $static) / ($frames - 1);    @at-root {     @keyframes #{$name} {       // Animation logic here     }   } } 

在实际中,可以这样调用:

@include carousel-animation(   $frames: 5,   $static: 17.5% ); 

编译出来的CSS:

@keyframes carousel {   0%, 17.5% {     transform: translateX(0%);     filter: blur(0);   }   19.0625% {     filter: blur(2px);   }   20.625%, 38.125% {     transform: translateX(-20%);     filter: blur(0);   }   39.6875% {     filter: blur(2px);   }   41.25%, 58.75% {     transform: translateX(-40%);     filter: blur(0);   }   60.3125% {     filter: blur(2px);   }   61.875%, 79.375% {     transform: translateX(-60%);     filter: blur(0);   }   80.9375% {     filter: blur(2px);   }   82.5%, 100% {     transform: translateX(-80%);     filter: blur(0);   } } 

任务完成了。如果我们想在联系页面使用另一个动,可以这样使用:

@include carousel-animation(   $name: 'carousel-contact',   $frames: 3,   $static: 20% ); 

是不是十分完美。(^_^)。

总结

这想当的完美。而Harry当初的代码容易阅读,但真的不太容易维护。使用Sass的自动化计算和循环特性,可以变得更佳方便。当然,它也使代码变得更为复杂,但这也使得它易于维护和更新。使用起来也非常的简单。

正文到此结束
Loading...