使用CSS制作Heart动画
##简介
几个星期前,我发现Twitter上几乎每个人的star都转向了一颗心。无疑这是一个很大的讨论的话题之一···但是我所在意的是··这个动画的实现是否只用CSS就可以实现呢(不是单一的图片或者SVG)?
我知道这并不很重要,但是当这个想法出现的时候,我简直不能入睡直到可以想出一个可行的方案。
经过一些实验后,我终于有了我的答案。结果并不完美(大量的SCSS / CSS -大约400行),但是也是令人满意的(至少基于我的期望)。
现在我就要描述我制作的步骤。
首先,我将其分解为了三层:中心(.heart
),光环(.ring
)以及圆(.circles
),最后再将它们在一个容器内(.heart-wrapper
)组合在一起。之后我制作了每一个图层,为每个图层添加动画效果,最后将他们结合在一起。
绘制
Heart
第一部分是其中心部分(.heart
)。
我将整个中心区域分解成了四个矩形。
- 左上(TL)和右上(TR)分别占据宽度的
50%
,高度的25%
- 左下(BL)和右下(BR)分别占据宽度的
50%
,高度的75%
在每一个矩形区域内我都使用了一个伪元素(:after
),并且使用了border-radius
元素使每部分其尽可能的接近原来的形状。
之后我使用了color
和overflow:hidden
属性。
Ring
第二部分是光环部分(.ring
)。
这是一个使用了不同border-size
值以及width/height
值的简单的圆环元素。
Circles
第三部分是圆(.circles
)。
我制作这一部分的时候,在中间部分使用了一个透明的圆形元素并且为其添加了盒子阴影。
在每一个圆圈上面使用了以逗号分隔的shadow-box
属性, x / y
位置是使用Compass利用sin / cos计算得到的角度值。
有关于
sin()
和cos()
相关的函数,可以使用Sassy Math库,里面涵盖了很多数学相关的函数功能。
动画
Heart
这一部分并不是很复杂。
增加/减少main
元素(并根据设置位置的left / top
)的width / height
属性值。唯一需要确定的事情是内部的元素是根据这个元素进行调整的。
Ring
调整border
的大小,内部圆环的大小以及其position
,同时还要注意color
的设置。
Circles
这一部分有些棘手,因为所有的border-shadow
随着时间一直改变(x/y
位置,大小,颜色),并且只有一个使用逗号分隔值的属性。
例如:
51.85185% {
box-shadow:
-8.48528em -8.48528em 0 -0.83333em #a068ce,
-8.38671em -5.44639em 0 -0.83333em #b752e1,
1.34357em -11.92455em 0 -0.83333em #99e9c8,
-0.97087em -9.95276em 0 -0.83333em #bae3d7,
10.16069em -6.38438em 0 -0.83333em #d3f491,
7.17606em -6.9645em 0 -0.83333em #dce483,
11.3266em 3.96335em 0 -0.83333em #59c392,
9.91926em 1.26817em 0 -0.83333em #67cd9f,
3.96335em 11.3266em 0 -0.83333em #caadc7,
5.19306em 8.54588em 0 -0.83333em #959ff3,
-6.38438em 10.16069em 0 -0.83333em #ca5ed8,
-3.44362em 9.38837em 0 -0.83333em #a975d1,
-11.92455em 1.34357em 0 -0.83333em #c35dd1,
-9.48718em 3.16122em 0 -0.83333em #90e0be;
}
这仅仅是其中的一步···
为了使其更加方便阅读和调整,我创建了一个Sass函数处理这个问题:
@function setBoxShadow($distance1, $distance2, $size1, $size2, $shiftAngle, $colorRatio) {
$boxS: ();
@for $i from 1 through length($circles) {
$circle: nth($circles, $i);
$order: $i - 1;
$angle1: ($order * $angleBetweenCircles) + $shiftAngleBeginning;
$angle2: $angle1 + $shiftAngle;
$distanceRatio1: $size * $distance1;
$distanceRatio2: $size * $distance2;
$firstCircle: map-get($circle, first);
$firstCircleStart: map-get($firstCircle, start);
$firstCircleEnd: map-get($firstCircle, end);
$secondCircle: map-get($circle, second);
$secondCircleStart: map-get($secondCircle, start);
$secondCircleEnd: map-get($secondCircle, end);
$boxS: append($boxS,
cos($angle1) * $distanceRatio1
sin($angle1) * $distanceRatio1
0
$circleSize * $size1
mix($firstCircleStart, $firstCircleEnd, $colorRatio)
);
$boxS: append($boxS,
cos($angle2) * $distanceRatio2
sin($angle2) * $distanceRatio2
0
$circleSize * $size2
mix($secondCircleStart, $secondCircleEnd, $colorRatio)
);
}
@return join($boxS, (), "comma");
}
这个函数遍历所有圆环(存储在一个Sass Map之上),并且相应的进行两两的设置box-shadow
属性值(大圆和小圆)
。基于距离的大小,通过传递参数转变他们之间的大小、颜色以及角度。
总结
之后我根据由@chrismabry书写的 https://medium.com/@chrismabry/how-did-they-do-that-the-twitter-like-animation-2a473b658e43这篇文章进行了动画的时间/百分比。
因此我将其分解了28个步骤
$animStep: 100% / 27;
运行的时间为0.8s
$animDuration: 0.8s;
使用了一个函数生成每个步骤的百分比:
@function setStep($n) { @return ($n — 1) * $animStep }
例如:
#{setStep(6)}
输出为:
18.51852%
现在就可以将三个部分进行合并。
下面的这张图片将胜于我的冗长的解释:
这时动画的激活/暂停是通过在容器上添加/删除类(.active
)实现的。在我的示例中是通过点击切换类。
结论
我实现了我的目标,但是你可能会想我仅仅完成了一个自我挑战,别的并没有什么。
目前浏览器支持的版本是当前版本(IE10+),因为这个示例的实现大部分是基于CSS3的动画http://caniuse.com/#feat=css-animation。
其效果大致如下:
我尽可能的使代码简洁整齐,并且我在顶部设置了Sass变量,方便人们进行一些测试以及更改。如果你有更好的想法,请让我知道(将JS中的debug设置为true
,你就可以观看动画一步一步实现的过程)。
本文根据@Nicolas Escoffier的《Twitter’s Heart Animation in Full CSS》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://blog.prototypr.io/twitter-s-heart-animation-in-full-css-b1c00ca5b774#.p31ds62ns。
如需转载,烦请注明出处:https://www.fedev.cn/animation/twitter-s-heart-animation-in-full-css.htmlAir Max 90 Ultra 2.0 Flyknit