svg 的描边动画
闲逛时看到北京大学一个有趣的动画,详细链接请点此进入,在最底部有一个 svg 的动画。看着挺有趣的,就想着实现一下。像类似的动画大概率都是 canvas 或者 svg 绘制的,f12 一看果然是 svg。
svg 是什么
可缩放矢量图形(Scalable Vector Graphics,SVG)基于 XML 标记语言,用于描述二维的矢量图形。
stroke-dasharray
属性 stroke-dasharray 可控制用来描边的点划线的图案范式。作为一个外观属性,它也可以直接用作一个 CSS 样式表内部的属性。
它是一个数列,数与数之间用逗号或者空白隔开,指定短划线
和缺口
的长度。如果提供了奇数个值,则这个值的数列重复一次,从而变成偶数个值。
以下为示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>stroke-dasharray</title>
<style>
.icon {
width: 200px;
height: 200px;
}
.p {
stroke: black;
stroke-width: 5;
stroke-dasharray: 10 5;
}
</style>
</head>
<body>
<svg class="icon">
<line class="p" x1="0" y1="50%" x2="100%" y2="50%"></line>
</svg>
</body>
</html>
stroke-dashoffset
stroke-dashoffset 属性指定了 dash 模式到路径开始的距离,即偏移位置。
以下为示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>stroke-dasharray</title>
<style>
.icon {
width: 200px;
height: 200px;
}
.p {
stroke: black;
stroke-width: 5;
/* 实线为整长 */
stroke-dasharray: 200;
/* 偏移量 */
stroke-dashoffset: 100;
}
</style>
</head>
<body>
<svg class="icon">
<line class="p" x1="0" y1="50%" x2="100%" y2="50%"></line>
</svg>
</body>
</html>
红色为整长,由图可知偏移了一半。
获取 svg 路径总长
getTotalLength() 该方法返回用户代理对路径总长度(以用户单位为单位)的计算值。
完整代码
页面渲染后绘制一遍动画,当鼠标放至图上时通过替换节点
重新绘制动画。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>svg的描边动画</title>
<style>
path {
/* 移除填充颜色 */
fill: none;
/* 设置线的颜色 */
stroke: skyblue;
}
.icon {
width: 200px;
height: 200px;
cursor: pointer;
}
.p {
stroke-dasharray: var(--l);
stroke-dashoffset: var(--l);
animation: stroke 1s forwards;
}
@keyframes stroke {
to {
/* 动画结束时,偏移量为0 */
stroke-dashoffset: 0;
}
}
</style>
</head>
<body>
<svg class="icon" x="0px" y="0px" viewBox="0 0 90 85">
<g id="pathGroup">
<path
class="p"
d="M23.87,81.36V56.69c0-0.11-0.05-0.21-0.14-0.28l-7.64-5.72c-0.11-0.08-0.25-0.09-0.37-0.03L3.18,57.42
C3.07,57.48,3,57.6,3,57.73v23.29c0,0.19,0.15,0.34,0.34,0.34h83.54c0.19,0,0.34-0.15,0.34-0.34V54.99c0-0.12-0.06-0.23-0.17-0.3
l-10.05-6.01c-0.1-0.06-0.17-0.17-0.17-0.3V36c0-0.11-0.05-0.21-0.14-0.28L60.97,24.18c-0.12-0.08-0.27-0.09-0.39-0.01L41.64,36.61
c-0.23,0.15-0.53-0.01-0.53-0.29l0.15-31.14c0-0.3-0.36-0.46-0.58-0.25l-16.7,16.22c-0.07,0.06-0.1,0.15-0.1,0.25v38.11"
></path>
<path
class="p"
d="M41.26,5.18l12.72,10.11c0.08,0.07,0.12,0.16,0.12,0.26v65.81"
></path>
<path
class="p"
d="M54.05,45.57L38.04,51.1c-0.14,0.05-0.23,0.18-0.23,0.33v5.05c0,0.23,0.22,0.39,0.44,0.33l15.8-4.49"
></path>
<path
class="p"
d="M54.05,61.75L38.08,65.3c-0.16,0.03-0.27,0.17-0.27,0.34v5.71c0,0.21,0.19,0.37,0.4,0.34l15.84-2.57"
></path>
</g>
</svg>
<script>
// 选择所有的路径元素
const paths = document.querySelectorAll(".icon .p");
paths.forEach((path) => {
// 获取每个路径的长度
const len = path.getTotalLength();
// 将路径的长度设置为CSS变量
path.style.setProperty("--l", len);
});
// 定义一个函数来重新开始动画
const restartAnimation = () => {
// 获取包含所有路径的组
const pathGroup = document.getElementById("pathGroup");
// 克隆这个组
const cloneGroup = pathGroup.cloneNode(true);
// 用克隆的组替换原始的组,这将重新开始动画
pathGroup.parentNode.replaceChild(cloneGroup, pathGroup);
};
// 获取SVG图标
const icon = document.querySelector(".icon");
// 当鼠标悬停在图标上时,重新开始动画
icon.addEventListener("mouseover", restartAnimation);
</script>
</body>
</html>