在 SVG 中,如果我们想实现一个动画效果,可以使用 CSS,JS,或者直接使用 SVG 中自带的 animate
元素(SMIL)。
这里我们主要探讨SVG与CSS结合实现的一些常见动画效果。
(下面要使用到的SVG基础知识,在 SVG从入门到图标绘制和组件封装 和 SVG中的Transform详解---平移、旋转和缩放 中都有详细的介绍,这里就不重复了,有需要的朋友可以前往查看哦。 )
SVG + CSS 动画实现的基础
HTML5 支持内联 SVG,我们可以将SVG元素
作为html
标签的一种,直接在页面结构中使用,成为 DOM 的一部分,这也使得我们可以用 CSS 对其做样式开发,这也是SVG + CSS 动画实现的基础。
上面的代码片段在页面绘制一个宽高100px的金色正方形。这里使用标签属性的方式描述它的宽度、高度和填充色,其实我们也可以在CSS中写这些样式。
rect{
width: 100px;
height: 100px;
fill: gold;
}
既然能用CSS对SVG做样式开发,那么结合animation
、transition
、transform
,使SVG元素的样式进行动态变换,就可以达到我们想要的动画效果。
rect{
width: 100px;
height: 100px;
fill: gold;
transition: fill 1s linear;
}
rect:hover{
fill: greenyellow;
}
基于transform的形状变换动画
和普通的HTML元素一样,SVG元素可以通过transform
进行平移、旋转和缩放等形状变换。
但两者的变换参考点不同。对于普通的HTML元素,变换的参考点,默认值是元素自身在x、y方向的中心位置50% 50%(这里仅考虑二维平面),也就是元素的旋转、移位、缩放等操作都是以元素自身在x、y方向的中心位置进行的。
而SVG元素,变换的参考点,是在SVG画布的0 0
的位置(默认是元素的左上角)。
理解这一点对于理解SVG元素的transform
变换非常重要,我们在前面的两篇文章中进行了非常详尽的讲解:
仿B站直播图标
动画实现
使用
标签绘制三条竖直的线条,(x1,y1)是线条起始点的坐标,(x2,y2)是线条终点的坐标。
(为了方便观察,我们给svg设置一个蓝色边框。)
接着,通过keyframes动画,不断改变scaleY
,让线条在Y方向进行缩放。
.beat{
transform-origin: bottom; //将变换参考点设置成`
最后,通过设置animation-delay
,让三段线条交错运动。
.beat:nth-child(1){
animation-delay: 0.4s;
}
.beat:nth-child(2){
animation-delay: 0.2s;
}
组件封装
实现了动画,接下来就是把图标进行组件封装,以便一次定义,多处引用。
参考:SVG组件封装
两个改造点:
- 1、将
标签都放到
中,
标签设置viewBox="0 0 100 100"
; 2、将
stroke="lightblue"
改成stroke="currentColor"
,在使用svg
图标时,颜色就会从父元素的color
属性继承。使用:
直播中
加载时钟
标签绘制时钟轮廓,两个
绘制长针和短针。
长短针的旋转,我们希望绕图标自身中心点进行。
我们知道,SVG元素变换的参考点,是在SVG画布的0 0
的位置。那么如果图标的中心点与SVG画布0 0
的位置重叠,那图标岂不就绕自身中心点进行旋转。
因此,我们将
标签圆心坐标设为(0,0)
(cx="0" cy="0"
),同时长短针的起点坐标也设为(0,0)
。为了图形显示完全,设置元素的
viewBox
属性为"-52 -52 104 104"
动画的实现就是通过keyframes动画,不断改变长短针的rotate角度
,由于两者旋转速度不同,所以动画时间设置不同。
.fast-hand{
animation: clock-rotate 2s linear infinite; /*动画时间设置不同*/
}
.slow-hand{
animation: clock-rotate 15s linear infinite; /*动画时间设置不同*/
}
@keyframes clock-rotate{
0%{
transform: rotate(0deg);
}
100%{
transform: rotate(360deg);
}
}
封装成组件的代码和使用案例如下:
等待中
描边动画
常用 SVG + CSS 来实现的,除了transform + animation/transition
这种组合实现图标的形状变换动画,另一种用处非常广泛的就是描边动画。
stroke-dasharray & stroke-dashoffset
描边动画的核心是 SVG 的两个显示属性,分别是 stroke-dasharray
和stroke-dashoffset
。
stroke-dasharray用于创建虚线。它的值是一个序列,可以传入多个值,分别指定虚线中线段和间隔的长度。
stroke-dasharray = '10, 20'
表示:线段10,间距20,然后重复 线段10,间距20。。。
该参数序列可以是一到多个数值,当数值的个数为奇数时,会自动复制一份,再生效。
比如stroke-dasharray = '10'
相当于stroke-dasharray = '10, 10'
;
stroke-dasharray = '10, 20, 30'
相当于stroke-dasharray = '10, 20, 30, 10, 20, 30'
,此时的绘制规则是:线段10,间距20,线段30,间距10,线段20,间距30,然后重复。。。
stroke-dashoffset: 描述相对于起始点的偏移。它的值是一个数值X,X>0
时,相当于往左移动了X个长度单位; X<0
时,相当于往右移动了X个长度单位。
stroke-dashoffset
只有在设置了stroke-dasharray
的情况下,才生效,非虚线的话,是无法看出偏移的。
对于一条线l
,如果设置stroke-dasharray = 'l.length'
,那么显示出来的就只有线段,没有间隔,相当于实线效果。而此时如果stroke-dashoffset='l.length'
,那线l
就往左移动了l.length
个长度单位,显示出来的就只有间隔,没有线段,相当于空白的效果。
如果stroke-dashoffset
的值从l.length
-->0
,线段就会逐渐显示出来。从而产生描边的效果。
描边效果的动画在开发中有很多应用场景,比如各种形状的进度条,图标或文字的描边,以及一些酷炫的按钮边框动画等。
案例实现
环形进度条
首先,我们将stroke-dasharray
和stroke-dashoffset
都设为圆环的周长;然后根据进度progress计算动态计算出新的stroke-dashoffset
,即(1-progress) * 圆环的周长
。
计算圆环周长的方法:
- 1、在已知半径的情况下,可以根据公式
2*Pi*r
求得周长。 2、SVG的形状元素都有一个
getTotalLength
的方法,可以获取该形状的路径总长度,对于规则和不规则的形状都适用。//页面结构
调整进度://js代码,获取圆环周长,并在调整进度后改变蓝色圆环的stroke-dashoffset
//css代码 .progress{ display: inline-block; position: relative; } .progress::before{ content: attr(data-percent); position: absolute; width: 100%; top: 50%; left: 0; transform: translateY(-50%); font-size: 20px; text-align: center; } .progress::after{ content: attr(data-name); position: absolute; width: 100%; top: 100%; left: 0; font-size: 25px; text-align: center; } .process-circle{ stroke-dashoffset:251; transition: stroke-dashoffset 3s; } .adjust{ margin-top: 50px; }
环形加载动画
stroke-dashoffset
和animation
结合还可以实现环形的加载动画,这是一种非常常见的加载动画。.progress{ stroke: #F7C223; animation: move 2s linear infinite; } .container{ animation: container 2s linear infinite; } //给外框也加上旋转动画,两个旋转叠加,效果更自然 @keyframes container { 0% { transform: rotate(0deg); } 100% { transform: rotate(270deg); } } @keyframes move{ //在改变stroke-dashoffset的同时也让圆环旋转 0%{ stroke-dashoffset: 251px; } 50%{ stroke-dashoffset: calc(251px * 0.2); transform:rotate(135deg); } 100%{ stroke-dashoffset: 251px; transform:rotate(450deg); } }
还可以再加上变色的动画,效果更绚丽。
.progress{
stroke: #F7C223;
animation:
move 2s linear infinite,
color-change 2s linear infinite;
}
@keyframes color-change {
0% { stroke: #4285F4; }
25% { stroke: #DE3E35; }
50% { stroke: #F7C223; }
75% { stroke: #1B9A59; }
100% { stroke: #4285F4; }
}
文字描边
以描边动画的方式出场可以使人对logo或者文字的印象更加深刻。
这里每个字母都是由path
元素绘制的图标,这种比较复杂的图形一般是由设计软件绘制,然后生成svg代码,我们这里使用的是figma。
对于这种不规则图形,只能用getTotalLength
方法可以获取该形状的路径总长度,然后设置stroke-dasharray
和stroke-dashoffset
的值。
const words = document.querySelectorAll('path')
for(let i=0; i
body{
background: #2e4057;
display: flex;
justify-content: center;
align-items: center;
}
svg{
stroke: hsl(189, 68%, 75%);
stroke-width:1px;
fill:hsl(189, 68%, 75%, 0%);
animation: color-change 1s ease-in forwards 3.8s;
}
//每个字母的描边动画执行时间和开始时间不同
path:nth-child(1){
stroke-dasharray: 246;
stroke-dashoffset: 246;
animation: show 1s linear forwards;
}
path:nth-child(2){
stroke-dasharray: 253;
stroke-dashoffset: 253;
animation: show 1.2s linear forwards .5s;
}
path:nth-child(3){
stroke-dasharray: 334;
stroke-dashoffset: 334;
animation: show 1.4s linear forwards 1s;
}
path:nth-child(4){
stroke-dasharray: 246;
stroke-dashoffset: 246;
animation: show 1.6s linear forwards 1.5s;
}
path:nth-child(5){
stroke-dasharray: 240;
stroke-dashoffset: 240;
animation: show 1.8s linear forwards 2s;
}
@keyframes show{
to{
stroke-dashoffset: 0;
}
}
@keyframes color-change{
to{
stroke: transparent;
fill:hsl(189, 68%, 75%)
}
}
按钮hover效果
非hover状态:stroke-dasharray: 170 540
,线段和间隔分别为170和540;stroke-dashoffset: -459
表示相对于起始点(矩形左上角)向右偏移(顺时针偏移)459,此时线段绘制在文字的正下方。
hover状态:stroke-dasharray: 760
,760为矩形的周长,此时stroke-dashoffset: 0
不偏移,绘制效果为一个完整的矩形。
Check it!
html, body {
background: #333;
height: 100%;
overflow: hidden;
text-align: center;
}
.wrapper {
height: 60px;
margin: 0 auto;
position: relative;
top: 50%;
transform: translateY(-50%);
width: 320px;
}
.border-rect {
fill: transparent;
stroke-dasharray: 170 540;
stroke-dashoffset: -459;
stroke-width: 8px;
stroke: #d20be4;
transition: all linear 0.5s;
}
.text {
color: #fff;
font-family: 'Roboto Condensed';
font-size: 22px;
letter-spacing: 8px;
line-height: 32px;
position: relative;
top: -48px;
}
.wrapper:hover .border-rect {
stroke-dasharray: 760;
stroke-dashoffset: 0;
stroke-width: 2px;
}
总结
本文主要探讨了SVG结合CSS实现动画的基础,以及一些常见动画的实现方式。随着浏览器对web标准支持的越来越好,SVG的使用也越来越方便,在做网站交互的时候,可以适当使用SVG动画提升你的网站表现力。