5月27日 15:43

SVG 如何与 CSS 结合使用?8 种方式从基础到高级动画

SVG 不仅是矢量图形格式,它和 CSS 的结合才是真正释放 SVG 威力的关键。内联 SVG 是 DOM 的一部分,每个形状、路径、文字都可以被 CSS 选中并施加样式、过渡和动画——这是 PNG、WebP 等位图永远做不到的。

内联 SVG 是前提

只有内联 SVG(直接写在 HTML 中的 <svg> 标签)才能被 CSS 完整控制。通过 <img> 引入的 SVG,外部 CSS 无法选中其内部元素,伪类和动画也会失效。所以如果需要用 CSS 操控 SVG,必须用内联方式。

1. 用 CSS 属性替代 SVG 属性

SVG 元素支持通过 CSS 设置视觉属性,fillstrokestroke-widthopacity 等都可以写在 CSS 规则里,和设置 HTML 元素的 colorbackground 没有本质区别:

css
.icon-circle { fill: #3b82f6; stroke: #1e40af; stroke-width: 2; opacity: 0.9; }
html
<svg viewBox="0 0 100 100" width="100" height="100"> <circle class="icon-circle" cx="50" cy="50" r="40" /> </svg>

注意:CSS 属性会覆盖 SVG 元素上的同名属性(style 优先级高于 presentation attributes),所以把样式集中到 CSS 里更好维护。

2. 用类名和选择器精确控制

SVG 元素和 HTML 一样支持 classid,CSS 的各种选择器都能用:

css
/* 类选择器 */ .logo-path { fill: #111; } /* 后代选择器 */ .nav-icon .highlight { fill: #f59e0b; } /* 属性选择器 */ circle[data-state="active"] { fill: #10b981; } /* :nth-child */ .chart-bar:nth-child(odd) { fill: #6366f1; } .chart-bar:nth-child(even) { fill: #818cf8; }

灵活运用选择器,可以避免给每个 SVG 元素加类名,减少标记冗余。

3. 伪类实现交互反馈

:hover:focus:active 对 SVG 元素完全有效,配合 transition 就能做出丝滑的交互效果:

css
.btn-icon { fill: #64748b; transition: fill 0.2s, transform 0.2s; cursor: pointer; } .btn-icon:hover { fill: #3b82f6; transform: scale(1.15); } .btn-icon:focus-visible { outline: 2px solid #3b82f6; outline-offset: 3px; }
html
<svg viewBox="0 0 24 24" width="24" height="24"> <path class="btn-icon" tabindex="0" d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 22 12 18.56 5.82 22 7 14.14l-5-4.87 6.91-1.01z" /> </svg>

tabindex="0" 让 SVG 元素可聚焦,配合 :focus-visible 提升键盘可访问性。实际项目中,图标 hover 变色、按钮按下缩放都是这么做的。

4. CSS 过渡与关键帧动画

过渡(transition)

过渡适合状态切换——hover 时变色、展开时位移,简单高效:

css
.sidebar-arrow { transition: transform 0.3s ease; transform-origin: center; } .sidebar-arrow.open { transform: rotate(90deg); }

JavaScript 切换 .open 类名即可,不需要操作 SVG 属性。

关键帧动画(@keyframes)

需要持续或循环的效果用 @keyframes

css
.spinner { animation: spin 1s linear infinite; transform-origin: center; } @keyframes spin { to { transform: rotate(360deg); } }
css
.pulse-dot { animation: pulse 1.5s ease-in-out infinite; } @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.3); opacity: 0.6; } }

加载旋转、呼吸闪烁,这些是最常见的 SVG CSS 动画场景。

5. 描边动画:stroke-dasharray 与 stroke-dashoffset

这是 SVG CSS 动画里最出效果的一招。原理很简单:先让 stroke-dasharray 等于路径总长度,整条线变成虚线且间距等于线长,视觉上不可见;然后通过 stroke-dashoffset 从线长过渡到 0,线就"画"出来了。

css
.draw-path { stroke-dasharray: 300; stroke-dashoffset: 300; animation: draw 2s ease forwards; } @keyframes draw { to { stroke-dashoffset: 0; } }
html
<svg viewBox="0 0 200 100" width="200"> <path class="draw-path" d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" fill="none" stroke="#3b82f6" stroke-width="3" stroke-linecap="round" /> </svg>

路径总长度可以通过 JavaScript 的 path.getTotalLength() 获取。实际开发中,Logo 描边动画、数据可视化图表的绘制效果,都是这个技术。

6. CSS 变量动态控制样式

CSS 变量让 SVG 样式变得可配置,一套图形换个主题色只需改几个变量:

css
:root { --icon-primary: #3b82f6; --icon-stroke: #1e40af; --icon-hover: #ef4444; } .themed-icon { fill: var(--icon-primary); stroke: var(--icon-stroke); transition: fill 0.2s; } .themed-icon:hover { fill: var(--icon-hover); }

在暗色模式下覆盖变量即可,不用写重复的选择器:

css
@media (prefers-color-scheme: dark) { :root { --icon-primary: #60a5fa; --icon-stroke: #93c5fd; --icon-hover: #f87171; } }

7. 外部样式表与样式分离

小型项目可以在 SVG 的 <style> 标签里写 CSS,但项目规模大了以后,把 SVG 样式抽到外部 CSS 文件更合理——和 HTML 样式统一管理,方便复用和压缩:

html
<!-- HTML --> <link rel="stylesheet" href="svg-styles.css" /> <svg viewBox="0 0 24 24" class="icon"> <path class="icon-path" d="..." /> </svg>
css
/* svg-styles.css */ .icon { width: 24px; height: 24px; } .icon-path { fill: currentColor; transition: fill 0.2s; } .icon:hover .icon-path { fill: #3b82f6; }

currentColor 是个实用技巧——SVG 的 fill 继承父元素的 color,这样改文字颜色就能同步改图标颜色。

8. 响应式 SVG 与媒体查询

SVG 配合 viewBox 和 CSS 媒体查询,可以实现真正的响应式图形:

html
<svg viewBox="0 0 400 200" width="100%"> <rect class="responsive-rect" x="10" y="10" width="180" height="180" rx="8" /> <text class="label" x="100" y="105" text-anchor="middle">Hello</text> </svg>
css
.responsive-rect { fill: #3b82f6; transition: fill 0.3s; } .label { fill: white; font-size: 16px; } @media (max-width: 600px) { .responsive-rect { fill: #ef4444; } .label { font-size: 12px; } }

viewBox 让 SVG 自适应容器宽度,媒体查询根据屏幕尺寸调整样式,两者配合不需要 JavaScript。

性能注意事项

  • 优先用 transformopacity 做动画,这两个属性不触发重排(reflow),GPU 加速友好。fillstroke 等属性的变化会触发重绘(repaint),大量元素同时动画时可能掉帧。
  • 避免对大量 SVG 元素同时施加复杂动画,可以用 will-change: transform 提示浏览器提前优化,但不要滥用。
  • <img> 引入的 SVG 无法用外部 CSS 控制,需要交互和动画时必须内联。但内联 SVG 会增加 DOM 节点,大型图表类 SVG(数百个节点)要考虑虚拟滚动或懒加载。
  • stroke-dashoffset 动画在低端设备上可能卡顿,路径越长越明显,必要时用 requestAnimationFrame 替代纯 CSS 方案。

核心就一点:内联 SVG 的每个元素都是 DOM 节点,CSS 能对 HTML 做的事,对 SVG 照样做。掌握选择器、过渡、关键帧、描边动画这四样,基本覆盖日常开发 90% 的需求。

标签:SVG