svg动画

155 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

stroke-dasharray与stroke-dashoffset

stroke-dasharray

stroke-dasharray用于创建虚线,值可以有多个。

stroke-dasharray为一个参数时stroke-dasharray = '10' 表示:实线10,间距也是10;两个参数或者多个参数时,如:stroke-dasharray = '10, 5' 表示:实线10,间距5,依次循环。

stroke-dashoffset

stroke-dashoffset是偏移的意思。

这个属性是相对于起始点的偏移,正数偏移x值的时候,相当于往左移动了x个长度单位,负数偏移x的时候,相当于往右移动了x个长度单位。

做一个动画:线段从无到有,由短变长

第一步:设置stroke-dasharray实线长度等于当前svg的宽度,间隔的长度大于或者等于svg宽度

<svg>
   <line id="line" x1="30" y1="30" x2="300" y2="30" stroke-width="20" stroke="red" stroke-dasharray="300"/>
</svg>

svg7.jpg

第二步:设置stroke左移300单位(stroke-dashoffset:300),也就是刚好隐藏了实线,可视区域内变成了300单位的间隔。

第三步:当鼠标移入的时候,使offset由300变成0,就实现了动图中的效果:

svg:hover #line {
  stroke-dashoffset: 0;
}
#line {
  transition: all 2s;
}
<svg>
  <line
    id="line"
    x1="30"
    y1="30"
    x2="300"
    y2="30"
    stroke-width="20"
    stroke="red"
    stroke-dasharray="300"
    stroke-dashoffset="300"
  />
</svg>

svg的transform 变换

  • translate 位移
<svg width="200" height="200" viewBox="0 0 200 200">
  <rect x="0" y="0" width="50" height="50" transform="translate(10,10)" />
</svg>
  • rotate 旋转
<svg width="200" height="200" viewBox="0 0 200 200">
  <rect x="0" y="0" width="50" height="50" transform="translate(50,50) rotate(30)" />
</svg>
  • scale 缩放
<svg width="200" height="200" viewBox="0 0 200 200">
  <rect x="0" y="0" width="50" height="50" transform="translate(50,50) scale(.5)" />
</svg>

环形进度条

.circle {
  animation: circle 5s linear infinite;
  transform-origin: center center;
}

@keyframes circle {
  from {
    stroke-dasharray: 0 1069;
  }
  to {
    stroke-dasharray: 1069 0;
  }
}
<svg width="440" height="440" viewbox="0 0 440 440">
  <circle
    cx="220"
    cy="220"
    r="170"
    stroke-width="50"
    stroke="#D1D3D7"
    fill="none"
  ></circle>
  <circle
    class="circle"
    cx="220"
    cy="220"
    r="170"
    stroke-width="50"
    stroke="#00A5E0"
    fill="none"
    // 从顶部开始
    transform="rotate(270)"
  />
</svg>

chrome-capture-2022-3-8 (2).gif

LOGO 描边

  1. 下载任意 SVG 格式的 LOGO
  2. 获取 path 长度
const path = document.getElementById('taobao-path')
const pathLen = path.getTotalLength()
  1. 添加描边样式和动画
.taobao-path {
  fill: none;
  stroke: #333;
  stroke-width: 1;
  animation: taobao 5s linear infinite forwards;
}
@keyframes taobao {
  from {
    stroke-dasharray: 8974;
    stroke-dashoffset: 8974;
  }
  to {
    stroke-dasharray: 8974;
    stroke-dashoffset: 0;
  }
}
<svg>
  <path
    id="taobao-path"
    class="taobao-path"
    d="M512 64l338.773333 76.330667A85.333333 85.333333 0 0 1 917.333333 223.573333v407.253334a170.666667 170.666667 0 0 1-86.506666 148.48l-276.757334 156.842666a85.333333 85.333333 0 0 1-84.138666 0L193.173333 779.285333A170.666667 170.666667 0 0 1 106.666667 630.826667V223.573333a85.333333 85.333333 0 0 1 66.56"
  ></path>
</svg>

chrome-capture-2022-3-8 (1).gif

SMIL

SMIL 全称 Synchronized Multimedia Integration Language,它允许我们通过 HTML 标签实现动画效果,它可以用于:

  • 实现过渡动画
  • 实现补间动画
  • 路径运动动画(CSS3无法实现)

SMIL 包含以下标签:

<set>
<animate>
<animateTransform>
<animateMotion>

为什么要用 SMIL?

实现动画的全新方式:

  • 无需 CSS
  • 无需 JS
  • 几个标签轻松实现复杂动画,它不香吗?

一个元素的动画属性

  • attributeName: 变动的属性的属性名
  • from: 变动的初始值
  • to: 变动的终值
  • dur: 动画的持续时间

set标签

实现属性的延迟设置,这样设置后,就每隔1s移动一端距离,不会有补间的动画,非常生硬。

<svg width="400" height="400">
  <rect x="0" y="0" width="100" height="100" fill="red">
    <set attributeName="x" attributeType="XML" to="10" begin="1s" />
    <set attributeName="x" attributeType="XML" to="20" begin="2s" />
    <set attributeName="x" attributeType="XML" to="30" begin="3s" />
    <set attributeName="x" attributeType="XML" to="40" begin="4s" />
    <set attributeName="x" attributeType="XML" to="50" begin="5s" />
  </rect>
</svg>

animation标签

如果你想在同一个元素里变动更多的属性,只要添加更多的元素

<svg width="500" height="200" viewBox="0 0 500 200">
  <circle cx="0" cy="0" r="30" fill="blue" stroke="black" stroke-width="1">
    <animate
      attributeName="cx"
      from="0"
      to="200"
      dur="5s"
      repeatCount="indefinite"
    />
    <animate
      attributeName="cy"
      from="0"
      to="200"
      dur="5s"
      repeatCount="indefinite"
    />
  </circle>
</svg>

svg01.gif

animateTransform 标签

<svg width="200" height="200" viewBox="0 0 200 200">
  <rect x="0" y="0" width="60" height="60" fill="red">
    <animateTransform
      attributeName="transform"
      begin="0s"
      dur="3s"
      type="scale"
      from="1"
      to="2"
      repeatCount="indefinite"
    />
  </rect>
</svg>

animateMotion 标签

可以自定义轨迹,然后沿着轨迹运动,这是css动画无法实现的

案例一:按 path 轨迹运动的正方形

rotate:设置沿轨迹交汇处转弯处的运动情况。小方块rect会沿着path="M 10 10 L 110 10 L 110 110 L 10 110 Z"做运动。

<svg width="200" height="200" viewBox="0 0 200 200">
  <rect x="0" y="0" width="10" height="10" fill="red">
    <animateMotion
      path="M 10 10 L 110 10 L 110 110 L 10 110 Z"
      dur="5s"
      rotate="auto"
      fill="freeze"
      repeatCount="indefinite"
    />
  </rect>
  <path
    id="motion-path"
    d="M 10 10 L 110 10 L 110 110 L 10 110 Z"
    fill="none"
    stroke="green"
  />
</svg>

chrome-capture-2022-3-8 (4).gif

案例二:混合动画

需求:小方块沿轨迹运动后,需要按原路返回。

begin开始的时间可以是上一次运动结束后再开始,animateMotion需要加上id。begin可以用分号分割多个值。

<svg viewBox="0 0 200 200" width="200" height="200">
  <rect x="0" y="0" rx="0" ry="0" width="10" height="10" fill="red">
    <animateMotion
      id="forward-rect"
      path="M 10 10 L 110 10 L 110 110 L 10 110"
      dur="2s"
      rotate="0"
      fill="freeze"
      begin="0; backward-rect.end + 0.5s"
    />
    <animateMotion
      id="backward-rect"
      path="M 10 110 L 110 110 L 110 10 L 10 10"
      dur="2s"
      rotate="0"
      fill="freeze"
      begin="forward-rect.end + 0.5s"
    />
  </rect>
  <path
    d="M 10 10 L 110 10 L 110 110 L 10 110"
    fill="none"
    stroke-width="1"
    stroke="blue"
  />
</svg>

chrome-capture-2022-3-8 (5).gif

上面两个动画可以形成一个无限循环的效果,首先forward-rect动画开始,第二个动画在第一个动画结束后0.5s开始运动(forward-rect.end + 0.5s),然后第一个动画在backward-rect结束后0.5s再开始运动。

案例三:点击变色或位移

begin还可以不止可以用时间来开始,还可以用click等事件进行开始,begin="rect1.click" 点击rect1的时候开始动画。

注意animate有个作用域的问题,如果放在rect的内部,那么只会对rect进行动画。如果放在svg下面,那么就对svg下面所有的图形进行动画。

<svg viewBox="0 0 200 200" width="200" height="200">
  <g id="rect1">
    <rect x="0" y="0" rx="0" ry="0" width="100" height="100" fill="red">
      <animate
        attributeType="XML"
        attributeName="fill"
        from="red"
        to="green"
        begin="rect1.click"
        dur="2s"
        fill="freeze"
      />
    </rect>
  </g>
  <animateTransform
    attributeType="XML"
    attributeName="transform"
    type="translate"
    from="0, 0"
    to="50, 50"
    begin="rect1.click"
    dur="2s"
    fill="freeze"
  />
  <rect x="0" y="100" width="100" height="100" fill="blue">
    <animate
      attributeType="XML"
      attributeName="fill"
      from="blue"
      to="green"
      begin="rect1.click"
      dur="2s"
      fill="freeze"
    />
  </rect>
</svg>

chrome-capture-2022-3-8 (6).gif