svg 交互动画 1

2024/01/11
共 2k 字
约 8 分钟
归档: 技术
标签: SVG

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>
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 里一个较难的分支,真要研究起来,无穷无尽。

留言

本站已运行
© 2024 Jack  由 Hexo 驱动
复制成功