Issac Lau

如何为 SVG 添加动画

January 22, 2025
8 min read
Loading

一个体验优秀的站点,往往 svg 图标是一个必不可少的元素, 因为它相比其他类型的图片有天然的很多优势。

因为其简单,灵活,可定制,可以很容易地加入动画。 利用好它的这一特性,往往可以给我们的站点增添不少灵动的气息。

本文将会介绍如何为 svg 添加动画,并且将会展示本站点使用的一些 svg 动画效果。

svg 101

svg 是一种基于 xml 的图形格式,它可以用于创建各种图形,例如圆形、矩形、椭圆、线条、文本等。

svg 文件中包含一个 <svg> 标签,这个标签包含了图形的定义,包括图形的形状、颜色、大小等。

下面是一个简单的 svg 文件:

App.tsx
function App() { return ( <svg style={{ background: "green" }} width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" > <circle cx="100" cy="100" r="80" fill="#FF0000" /> </svg> ); }

这个 svg 文件定义了一个圆形,其中的 cxcy 属性指定了圆形的中心点,r 属性指定了圆形的半径。 这里我想就 svg 元素本身的几个属性做下重点说明。

  • width 和 height 属性指定了 svg 的宽高。它们确定了 svg 元素整体在屏幕上所占空间的大小。
  • viewBox 属性指定了 svg 的视口,它是一个字符串,其格式是 x y width height,这里的 x 和 y 是视口的左上角的坐标,width 和 height 是视口的宽高。

也即是说,svg 的 width 与 height 属性决定了 svg 在 html 文档流中的大小。而 viewBox 属性则定义了 svg 内部各元素的参考坐标系。 viewBox 里的 width 和 height 和 svg 的 width 与 height 可以认为是两个完全独立的大小。互相没有任何关系。

viewBox 的 x 与 y 指定了视口左上角的坐标,如果不指定,则默认左上角的坐标为 (0, 0)。有时我们为了计算方便,会通过调整 viewBox 的 x 和 y 来将 svg 的中心位置设置为 0,这样就可以很方便的把 svg 元素完全居中。

App.tsx
function App() { return ( <svg style={{ background: "green" }} width="200" height="200" viewBox="-100 -100 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" > <circle cx="0" cy="0" r="20" fill="blue" /> </svg> ); }

这里我们通过将 viewBox 的 x 和 y 分别设置为 -100,-100,就恰好将 svg 的中心位置坐标变为了 (0, 0)。

viewBox 的 width 和 height 属性指定了 视口的宽高。他们与 viewBox 的 x 和 y 一起决定了 svg 元素内部各个子元素的参考坐标系。

App.tsx
function App() { return ( <svg style={{ background: "green" }} width="200" height="200" viewBox="-100 -100 100 100" fill="none" xmlns="http://www.w3.org/2000/svg" > <circle cx="0" cy="0" r="20" fill="blue" /> </svg> ); }

与上一个示例相比,我们改变了 viewBox 的 width 和 height。因为坐标系是从 (-100, -100) 开始的,而且我们在 viewBox 中定义了宽高分别为 100,也就是说我们的视口坐标系的右下角的坐标为 (0,0), 因此此处我们的圆形的圆心就在视口坐标系的右下角,所以就只能看到 1/4 的圆形。

关于 svg 本身,本文并不会展开讲解,如果你想要了解 svg 的基础知识,非常建议你去看看MDN, 或者如果想要通过示例学习,你可以去 SVG Tutorial 学习到非常多的关于 svg 的知识。

本站点用到的一些 svg 动画技术

圆形进度条

在博客列表页面,当我们鼠标移动到每个博客卡片上的时候,会有一个圆形进度条的动画效果。

HoverCircle.tsx
import { ArrowRightIcon } from "lucide-react"; function HoverCircle() { return ( <span className="group cursor-pointer relative inline-flex items-center justify-center shrink-0 w-8 h-8"> <svg className="absolute inset-0 w-full h-full text-primary/20" viewBox="0 0 100 100" > <circle cx="50" cy="50" r="48" fill="none" stroke="currentColor" strokeWidth="4" /> </svg> <svg className="absolute inset-0 w-full h-full invisible group-hover:visible" viewBox="0 0 100 100" > <circle cx="50" cy="50" r="48" fill="none" stroke="currentColor" strokeWidth="4" className="origin-center -rotate-90 text-primary transition-all duration-500 ease-in-out group-hover:[stroke-dashoffset:0]" strokeDasharray="301" strokeDashoffset="301" /> </svg> <ArrowRightIcon className="h-4 w-4 text-primary/50 relative z-10 transition-all duration-200 ease-in-out group-hover:text-primary group-hover:translate-x-1" /> </span> ); }

此进度条是用两个同心圆来实现的,底层的颜色较浅的圆充当了一个圆形轨道的作用。动画主要发生在上层颜色较深的圆上。 这里主要利用了 stroke-dasharraystroke-dashoffset

  • stroke-dasharray: 定义了用于绘制形状轮廓的虚线段和间隙的排列形式。这里 strokeDasharray="301" 实际上效果等同于 strokeDasharray="301,301" (奇数个的时候重复一遍) 也就是说在轮廓上先绘制 301 个长度的线段, 然后接着绘制 301 个长度的间隙。而这里的圆的半径是 48 ,所以 301 实际上是圆的周长(48 * 3.14 * 2)。那么绘制 301 长度的线段等同于绘制一个完整的圆。
  • stroke-dashoffset: 定义了虚线与路径起点之间的偏移量。它是一个可选的属性,如果没有指定,则默认为 0。也就是默认情况下,虚线和路径的起点在同一个位置。

而我们这里设置了 strokeDashoffset="301" 也就是将整个绘制都偏移出去了,也就是默认情况下看不到上层的这个圆的轮廓了,我们看到的是 301 长度的间隙。 而当我们的鼠标 hover 到卡片上的时候,我们设置了一个动画,动画的最终态是将 strokeDashoffset 重新设置成 0。也就是将圆的整个轮廓显示出来,这就达到了我们所要的圆形进度条的效果了。

组件内部的样式使用的是 tailwindcss,如感觉吃力,强烈推荐自行学习

也许你发现了每篇博客的点赞按钮图标,如果你好奇它是如何实现的,请持续关注我的博客,我将会在下次继续分享关于 svg 动画的实现。

评论

Loading