svg 交互动画 1
CSS 虽然能做动效,但微信公众号文章没法嵌入 JavaScript,导致没法实现点击后再触发动画。本以为任何交互效果都离不开 JS 监听(input[radio] 跟 details 元素虽然是个例外,但他们也没法用在公众号中使用),但偏偏有个古老的东西,能绕开这个限制,那就是 SVG 动画的 begin 属性。
animate
最常见的动画元素,放在形状内部即可生效,常用属性有:
attributeName: 必需的属性,要进行动画的属性名称。例如,“x”, “y”, “r”, “fill” 等
attributeType: 属性类型,可选值有
XML | CSS | auto
,默认值为 auto(先当成 CSS 处理,如果发现不认识,当作 XML 类别处理)from: 动画的起始值
to: 动画的结束值
values: 动画的值列表,这些值将按顺序播放。例如,“0;50;100” 将会先将属性值设置为0,然后设置为50,最后设置为100
dur: 动画的持续时间
repeatCount: 可选值有
<number> | indefinite
restart: 动画何时应该重新开始。可选值有
always | whenNotActive | never
fill: 动画结束后的行为。可选值有
freeze |remove
calcMode: 动画的计算模式。可选值有
discrete | linear | paced | spline
keyTimes:以分号分隔的时间值列表,用于控制动画的执行步骤列表中的每个值与 values 中的值一一对应,定义了 values 中的值在动画中何时执行,keyTimes 列表中的每一个值都是指定在 [0-1] 之间的浮点数,表示动画的完成时间。
keySplines: calcMode 为 spline 时,与 keyTimes 列表关联的贝塞尔曲线控制点
下面是一个通过改变不透明度,实现闪烁动画的例子。
values 有3个值,意味着有3个关键帧,keySplines 中的两组值,用来描述第一到第二、第二到第三个关键帧的过渡。
<svg xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="100" fill="lightcoral"></rect>
<text x="20" y="40" fill="white">SVG 纯色背景闪烁动画</text>
<animate attributeName="opacity"
calcMode="spline"
keySplines="0.42 0 0.58 1.0; 0.42 0 0.58 1.0"
values="0;1;0"
begin="0"
dur="4s"
repeatCount="indefinite"></animate>
</svg>
细说 begin 的属性值
SVG 最牛的地方在于,begin 的值可以为 click
,即点击时触发,实现了本该由 JS 监听才能做到的效果,除此之外,他还可以有以下值:
时间值:这是最常见的值类型,表示动画开始的时间点,单位为秒(s)或毫秒(ms)。例如,begin=“0s” 表示动画立即开始。
事件名称:除了 click,还可以有
- load 当页面加载完成时触发
- dblclick, mouseenter, mouseleave 等鼠标事件
- keydown, keypress, keyup 等键盘事件
- focus, blur 元素获得或失去焦点时触发
其他元素的动画结束时间:通过引用其他元素的动画 ID 和 .end 来设置动画开始的时间。例如,begin=“c1.end” 表示等待 ID 为 “c1” 的动画结束后再开始新动画。
同步基准(syncbase):使用其他元素的动画 ID 和 .begin 或 .end 来创建更复杂的同步效果。例如,begin=“first.end;third.end” 表示在 “first” 动画结束和 “third” 动画开始时同时开始新动画。
偏移量(offset):使用其他元素的动画 ID 和 .repeat() 方法来设置动画开始的时间。例如,begin=“myLoop.repeat(1)” 表示在 “myLoop” 动画重复一次后开始新动画。
除了监听固定按键触发动画等少数情况,上述属性能覆盖大部分需求,实现复杂动画。
点击消失效果
在上面例子的基础上,把 values 改成 from="1"
与 to="0"
,设置 begin="click"
,为了保持最后一帧的状态,还需设置 fill="freeze"
,可实现点击消失效果。
<svg xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="100" fill="lightcoral"></rect>
<text x="20" y="40" fill="white">点击消失效果</text>
<animate attributeName="opacity"
from="1" to="0" begin="click"
dur="1s" fill="freeze"></animate>
</svg>
把以上元素与动画用 g 标签包起来,让动画作用在这个分组上,底下再叠一层文字,即可实现常见的“点击查看”效果
<svg width="200" height="100" xmlns="http://www.w3.org/2000/svg">
<g>
<rect width="200" height="100" fill="lightblue"></rect>
<text x="50%" y="50%" dominant-baseline="middle"
text-anchor="middle" fill="white">这是答案</text>
</g>
<g>
<rect width="200" height="100" fill="lightcoral"></rect>
<text x="50%" y="50%" dominant-baseline="middle"
text-anchor="middle" fill="white">点击显示答案</text>
<animate attributeName="opacity" from="1" to="0"
begin="click" dur="1s" fill="freeze"></animate>
</g>
</svg>
set
上面的例子其实还有个小 bug:由于点击后只是把不透明度改为0,元素还在,再次点击会二次触发动画效果。有一个简单的解决方法,就是给动画设置 restart="never"
,让他只会触发一次,除非刷新重载页面。
当然,最合理的方案其实是让上层的元素“消失”。
这就轮到 set 元素出场,<set>
与 <animate>
的区别:
不需要 from 属性,起始状态即为父节点属性值
一到 begin 属性设置的时点,指定的 attributeName 属性值即改为 to 指定的属性值,没有过渡动画,animate 默认会添加线性动画
attributeName 的属性值可以为非数字,如 style=“visibility: hidden;”
在 CSS 里,我们常用 opacity
配合 visibility
做渐隐渐现动画,在不透明度变为 0 的瞬间,设置 visibility="hidden"
让元素消失无法被点击,这样效果就完美了:
<svg width="200" height="100" xmlns="http://www.w3.org/2000/svg">
<g>
<rect width="200" height="100" fill="lightblue"></rect>
<text x="50%" y="50%" dominant-baseline="middle"
text-anchor="middle" fill="white">这是答案</text>
</g>
<g>
<rect width="200" height="100" fill="lightcoral"></rect>
<text x="50%" y="50%" dominant-baseline="middle"
text-anchor="middle" fill="white">点击显示答案</text>
<animate attributeName="opacity" from="1" to="0"
begin="click" dur="1s" fill="freeze"></animate>
<set attributeName="visibility"
from="visible"
to="hidden"
fill="freeze"
begin="click+1s"/>
</g>
</svg>
animateTransform
animateTransform
元素主要用于创建对象的变换动画,包括平移、缩放、旋转和斜切等。主要属性有:
attributeName: 指定要应用动画的属性,通常是 transform
type: 指定要应用的变换类型,可以是 translate、scale、rotate、skewX 或 skewY
from 和 to: 指定变换的起始和结束值
还有 begin, dur, end, min, max, restart, repeatCount, repeatDur, fill,这些属性用于控制动画的开始时间、持续时间、结束时间、最小值、最大值、重新开始动画的时间、重复次数、重复持续时间和动画结束后的行为,基本与 animate 一致
单方向平移
<svg style="background-color: lightyellow" width="100" height="100">
<rect id="myRect" width="100" height="100" style="fill:skyblue;">
<animateTransform attributeName="transform"
type="translate"
values="0 0;-120 0"
begin="click"
dur="1s"
fill="freeze">
</animateTransform>
</rect>
</svg>
双开门效果
这里有一个小技巧,给外层设置 id="outer"
,触发条件为 begin="outer.click"
,可以实现点击外层元素触发内层元素进行动画。
<svg id="outer" style="background-color: lightyellow" width="100" height="100">
<g>
<rect width="50" height="100" fill="skyblue">
<animateTransform attributeName="transform"
type="translate"
values="0 0;-50 0"
begin="outer.click"
dur="1s"
fill="freeze">
</animateTransform>
</rect>
<rect x="50" width="50" height="100" fill="lightpink">
<animateTransform attributeName="transform"
type="translate"
values="0 0;50 0"
begin="outer.click"
dur="1s"
fill="freeze">
</animateTransform>
</rect>
<set attributeName="visibility"
from="visible"
to="hidden"
fill="freeze"
begin="outer.click+1s"/>
</g>
</svg>
小结
其实知道了 begin 属性可以为 click 后,就掌握了核心,动效的难点在于想象力,尤其是 svg 动画,算是 CSS 里一个较难的分支,真要研究起来,无穷无尽。
留言