1. 第一步 先构图, 费时 1 小时
画图这种事情, 怎么能难得到一个美工呢. 在 Ai 中完成, 不考虑太多细节(原效果中有渐变和噪点, 这里就省略了, 用纯色填充来代替, 毕竟人家是一个 team, 而我是一个人在摸索着战斗).
图做完之后, 就要进行拆解了, 需要把四个部分拆出来, 这四个部分就是要做动效的部分, 左大腿, 左小腿, 右大腿, 右小腿.
这四个部分为了方便下面做动效, 每一个都是要放到单独的图层中的, 至于其他图层顺序, 就不是那么重要了.
2. 第二步 给腿部增加旋转动效, 费时 0.5 小时
做这种涉及到关节类的实现效果, 其实是个通路, 因为简而言之都是旋转动画, 不过是增加了连动, 也就是说大腿围绕关节处 (即原点) 的旋转时要带动小腿围绕大腿末端 (小腿动画对应的原点) 的旋转. 因为有做舞动的机器人的基础, 这里就简单了很多, 不过仍然做了一点点小的优化, 没有像做机器人时拆分成很多 SVG, 而是直接进行了嵌套.
首先, 最最重要的一点, 要获取下面图中这三个坐标值. 这是做旋转动画时不可或缺的 tranform-origin 值.
大腿的旋转是非常简单的旋转动画设置了, 以左腿为例, 通过对比左右腿, 差不多得到的数据就是大概顺时针旋转了 60 度, 只要在 CSS 中定义动画参数如下:
- /* 大腿旋转运动的动画规则 */
- @keyframes legLMove{
- 0% {
- transform: rotate(0deg)
- }
- 100% {
- transform: rotate(-60deg)
- }
- }
- #legL{
- animation: legLMove 1s infinite alternate;
- transform-origin:455px 235px; /* 大腿旋转运动的原点 */
- }
这样就轻松的得到了大腿运动的动效.
相比之下, 小腿就要复杂的多, 步步拆解嘛, 动画设置的思路也是解题思路, 所以, 先解决的第一个问题, 就是小腿跟随大腿运动的问题. 因为在导出 SVG 之前, 在 Ai 中已经把需要增加动效的部分放置再来不同的图层里, 因此, 简化的腿部对应的 DOM 结构是下面这种
- <!-- 左大腿 -->
- <g id="legL">
- <path d="M......z"/>
- </g>
- <!-- 左小腿 -->
- <g id="calfL">
- <path d="M......z"/>
- </g>
既然要跟随大腿运动, 那好办, 直接把左小腿整体部分放到大腿所在的组合 < g > 标签中, 就能保证相同的运动了. 修改后的 DOM 结构就变成了:
- <!-- 左大腿 -->
- <g id="legL">
- <path d="M......z"/>
- <!-- 左小腿 -->
- <g id="calfL">
- <path d="M......z"/>
- </g>
- </g>
现在, 这一步已经解决了, 那么来解决下一个问题吧, 如何让小腿自由的摆动. 这里, 为了确定小腿的旋转参数, 我画了一张示意图.
这张图中, 绿色的弧线就是大腿的运动轨迹, 红色的弧线是小腿的运动轨迹, 最终小腿部分和大腿部分几乎是平直状态, 因为小腿目前是跟随大腿运动的, 换句话说, 它们两个处于同一个参考系中, 因此, 在确定小腿的摆动幅度时, 不用考虑大腿的状态.(半透明的小腿部分就是最终小腿的状态). 目测, 逆时针旋转, 100 度左右(不用那么精确). 因此, 小腿部分的动画规则就出来了:
- @keyframes calfLMove{
- 0% {
- transform: rotate(0deg)
- }
- 100% {
- transform: rotate(100deg)
- }
- }
- #calfL{
- animation: calfLMove 1s infinite alternate;
- /* 小腿对应的旋转原点 也就是大腿的末端 */
- transform-origin:343px 220px;
- }
这样, 小腿在跟随大腿进行旋转运动的同时实现了自己的旋转动效.(这里实现的这种方法要比之前在做舞动的机械人时优化了很多).
左腿完成了, 右腿就水到渠成了, 不过是一个逆向而已. 记得修改 DOM 结构.
当然, 这个蹬自行车的效果实现的并不好, 看上去更像是踩踏板, 有时间的话准备开一篇新的文章通过定义
offset-rotate:var(--degMove);
这种在 CSS 中设置随路径曲率的旋转方向为变量的方法, 然后 JavaScript 设置定时器改变这个值来实现(这个方法已经测试过了, JS 同样也可以通过
document.documentElement.style.setProperty
的方法来改变 SVG 的 CSS 样式). 此为后话.
3. 第三步 增加音符的动效, 费时 0.5 小时
为了尽可能的和原效果保持一致, 这里把音符的动效一并加上了. 为了方便查看, 我先把无关元素暂时隐藏掉, 只保留音符, 大概的效果是下面这种:
音符逐渐变大并沿路径移动后淡化消失的过程. 从这句话中, 我们提炼出三个元素: 沿路径移动 | 变大 | 变淡, 也就是说对应三种动效设置. 第一步, 为了实现路径动画, 我先绘制了路径, 并确保每个音符落在路径上.
路径动画的 CSS 参数写一写:
- @keyframes notePath{
- 0% {
- offset-distance:0%;
- }
- 100% {
- offset-distance:100%;
- }
- }
- #notePath{
- offset-path:path(' '); /* 绘制的路径 path 对应的 d 属性 */
- animation:notePath 2s ease infinite;
- }
音符先不做缩放的处理, 以中间状态的为基准, 调用这个动画属性后, 就可以得到沿路径运动的音符了 **(再次强调, 在路径动画中, 一定要把元素放到画布零位置后再导出.)**:
继续, 下面叠加尺寸变化, animation 属性不需要做任何修改, 但动画规则中要增加关于缩放, 也就是
transform:scale()的定义
- .
- @keyframes notePath{
- 0% {
- offset-distance:0%;
- transform:scale(0.2); /* 增加关于缩放的定义, 起点缩小至原尺寸 0.2*/
- }
- 100% {
- offset-distance:100%;
- transform:scale(1.5); /* 增加关于缩放的定义, 终点放大至原尺寸 1.5 倍 */
- }
- }
此时, 音符在沿路径移动的基础上已经叠加了缩放的效果:
只差最后一步, 就是透明度的变化了. 通过分析动效, 音符并不是在整个动画的时间过程中遵循透明度的变小的规律, 而是在变化的中途开始逐渐变淡. 这里的处理有两个思路, 正好都说一下. 第一种, 直接定义在统一的动画规则中. 既然是中途的变化, 那我们 CSS 可以改成下面这种:
- @keyframes notePath{
- 0% {
- offset-distance:0%;
- transform:scale(0.2);
- }
- 50%{
- opacity: 1
- } /* 在中途增加一个关于透明度的定义 */
- 100% {
- offset-distance:100%;
- transform:scale(1.5);
- opacity: 0.1
- }
- }
来个好玩的吧, 试试骗过浏览器. 其实浏览器是很傻的, 它只会乖乖的看着你的动画规则进行解析, 比如透明度这种问题, 我们知道 opacity:1 就是完全不透明的了, 但浏览器对于 opacity 定义大于 1 时, 也会按照完全不透明来解析. 想到了什么没?
也就是说, 我们完全不用增加一个 50% 的关键帧, 而是写成下面这种:
- @keyframes notePath{
- 0% {
- offset-distance:0%;
- transform:scale(0.2);
- opacity: 2; /* 透明度设置 > 1*/
- }
- 100% {
- offset-distance:100%;
- transform:scale(1.5);
- opacity: 0.1;
- }
- }
对浏览器来说, 它要执行的过程中, 透明度 opacity 是这样来的, 整个时间段, 2→0.1, 所以差不多前半段是 2→1(对应透明度不发生变化), 后半段 1→0.1(对于透明度变小), 也就满足了我们的设定. 第二种方法, 虽然略显麻烦, 却是很重要的一种方法. 在上面的设置中, 有一个问题, 就是因为 animation 属性是一个统一定义, 这里面包含的参数非常多, 比如说速率, 也就是我定义为 ease 值的参数. 如果我想让透明度线性速率变化, 怎么办呢? 来试试给动画属性叠加多个规则
- /* 增加一个设置透明度变化的动画规则 */
- @keyframes noteOpacity{
- 50%{opacity:1}
- 100%{opacity:0.1}
- }
这时, 我们要把这个透明度变化的动画规则追加到 animation 的定义中, 追加的方式很简单, 逗号后面增加上新的动画规则的属性就可以了:
- animation:notePath 2s ease infinite ,noteOpacity 2s linear infinite;
- /* 新的动画规则 noteOpacity 可以自由定义其他动画属性 */
这种叠加多个动画规则 (当然了, 你得有个前提, 这些动画规则分别定义了不同的属性变化) 在一些场景下非常有用的. 以这个来做一个非常简单的尝试. 当我把 noteOpacity 这个动画规则的动画时间由 2s 改成 0.5s, 为了看出差别, 路径动画的时间延迟到了 5s, 猜猜动效会发生什么变化?
正如图中所示, 这是两个不同的动画规则的叠加效果, 因此整个路径运动的过程中会有数次透明度的变化. 好了, 让律动的音符和我们骑自行车的动效进行合成吧!
粗制滥造的仿作到此为止. 来个小结吧, 这篇文章中两个重要的部分:
对于连动效果, 先把整体部分放入需要连动的元素的同级中, 以便保持相同的动画设置, 再另行设置单独的动画属性.
给动画属性 animation 增加多个不同的动画规则的方法非常非常有用.
我也是在做 CSS 动画的过程中, 不断来优化原来总结的知识体系, 以后遇到好玩有趣的案例都会做一做. 至于 CSS 变量这个, 虽然是 2 年前的功能, 对我来说, 却是新的知识点, 也准备试一下和 JS 结合之后能怎么玩起来, 毕竟要比通过 innerhtml 方法向 CSS 文件中添加 < style > 标签和内容要简单明朗的多.
来源: https://juejin.im/post/5c7f6e796fb9a049f154f59d