前置知识: HTML5

SVG

4 min中级

基本形状、viewBox、路径、文本、滤镜

前置知识

建议先阅读以下内容再进入本文:

前置要求:SVG 画图本身零基础可学(形状、路径、渐变都不需要 JS);涉及交互的示例需要 JavaScript 基础(javascript/001-005、039)。本章末尾的”SVG 与 Canvas 互转”需要先了解 006 的 Canvas 概念,第一遍可跳过。

1. SVG 基础

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
  <circle cx="100" cy="100" r="80" fill="blue" />
</svg>

2. viewBox 详解

viewBox="min-x min-y width height" 定义 SVG 的内部坐标系统:

<svg width="400" height="300" viewBox="0 0 200 150">
  <rect x="0" y="0" width="100" height="75" fill="blue" />
</svg>
preserveAspectRatio 值说明
xMidYMid meet居中,完整显示(默认)
xMidYMid slice居中,裁剪填充
none不保持比例

3. 基本形状

<svg width="400" height="300">
  <rect x="10" y="10" width="100" height="60" rx="10" fill="blue" />
  <circle cx="200" cy="80" r="50" fill="red" />
  <ellipse cx="320" cy="80" rx="60" ry="30" fill="green" />
  <line x1="10" y1="150" x2="390" y2="150" stroke="black" />
  <polyline points="10,180 50,160 90,200" fill="none" stroke="purple" />
  <polygon points="200,180 240,220 160,220" fill="orange" />
</svg>

4. 路径 path

命令说明示例
M移动到M 10 10
L直线到L 100 100
C三次贝塞尔C 20,20 40,20 50,10
Q二次贝塞尔Q 50,0 100,50
A弧线A 25,25 0 0,1 50,25
Z闭合路径Z

小写字母为相对坐标,大写字母为绝对坐标。

5. 文本

<svg>
  <text x="20" y="50" font-size="24" fill="black">Hello SVG</text>
  <defs><path id="curve" d="M 50 150 Q 200 50, 350 150" /></defs>
  <text font-size="20"><textPath href="#curve">沿曲线排列的文字</textPath></text>
</svg>

6. 滤镜与渐变

<svg>
  <defs>
    <filter id="blur"><feGaussianBlur stdDeviation="5" /></filter>
    <filter id="shadow"><feDropShadow dx="4" dy="4" stdDeviation="3" /></filter>
    <linearGradient id="lg" x1="0%" x2="100%">
      <stop offset="0%" stop-color="red" />
      <stop offset="100%" stop-color="blue" />
    </linearGradient>
  </defs>
  <rect x="50" y="50" width="100" height="80" fill="url(#lg)" filter="url(#shadow)" />
</svg>

7. SVG 与 Canvas 互转

实际项目偶尔需要”把 SVG 画到 Canvas 上”(例如做导出、加水印、截图合成)。原理是把 SVG 序列化成图片再画进 Canvas:

async function drawSvgToCanvas(svg, canvas) {
  const xml = new XMLSerializer().serializeToString(svg);
  const svgBlob = new Blob([xml], { type: 'image/svg+xml;charset=utf-8' });
  const url = URL.createObjectURL(svgBlob);

  const img = new Image();
  await new Promise((resolve, reject) => {
    img.onload = resolve;
    img.onerror = reject;
    img.src = url;
  });

  const ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  URL.revokeObjectURL(url); // 释放临时 URL
}

// 使用:把页面上 id 为 mySvg 的 SVG 画进 canvas
drawSvgToCanvas(document.getElementById('mySvg'), document.getElementById('myCanvas'));

讲解:

  1. XMLSerializer 把 SVG DOM 节点序列化成 XML 字符串。
  2. Blob + URL.createObjectURL 生成一个临时图片地址,Image 加载它。
  3. ctx.drawImage 把图片按画布尺寸绘制;SVG 是矢量,绘制到任意尺寸都不糊。
  4. revokeObjectURL 释放临时 URL,防止内存泄漏。
  5. 反向操作(Canvas → SVG)没有官方 API,通常是把 Canvas 导出为 PNG 再嵌入 <image>,或直接用 canvas.toDataURL() 生成位图。

第一遍可跳过:本节需要 JavaScript 异步基础(Promise),读完 javascript/023 再回来。

SVG 基础

svg 元素 <svg [width] [height] [viewBox="<min-x> <min-y> <width> <height>"] [xmlns="http://www.w3.org/2000/svg"]>...</svg>

<!-- 基础 SVG -->
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
  <circle cx="100" cy="100" r="80" fill="blue" />
</svg>

<!-- 内联 SVG(HTML 中可直接使用) -->
<svg width="100" height="100">
  <rect width="100" height="100" fill="red" />
</svg>

viewBox 坐标系统

viewBox 详解 viewBox="<min-x> <min-y> <width> <height>"

<!-- 200x150 内部坐标,显示为 400x300 -->
<svg width="400" height="300" viewBox="0 0 200 150">
  <rect x="0" y="0" width="100" height="75" fill="blue" />
</svg>

preserveAspectRatio 属性 preserveAspectRatio="[alignment] [meet|slice|none]"

<svg width="400" height="300" viewBox="0 0 200 150" preserveAspectRatio="xMidYMid meet">
  <rect width="200" height="150" fill="green" />
</svg>
preserveAspectRatio 值说明
xMidYMid meet居中,完整显示(默认)
xMidYMid slice居中,裁剪填充
xMinYMin meet左上对齐,完整显示
none不保持比例,拉伸填充

基本形状

矩形 rect <rect x="<X>" y="<Y>" width="<宽>" height="<高>" [rx="<圆角X>"] [ry="<圆角Y>"] [fill] [stroke] [stroke-width] />

<rect x="10" y="10" width="100" height="60" rx="10" ry="10" fill="blue" stroke="black" stroke-width="2" />

圆形 circle <circle cx="<圆心X>" cy="<圆心Y>" r="<半径>" [fill] [stroke] />

<circle cx="200" cy="80" r="50" fill="red" />

椭圆 ellipse <ellipse cx="<圆心X>" cy="<圆心Y>" rx="<X半径>" ry="<Y半径>" [fill] [stroke] />

<ellipse cx="320" cy="80" rx="60" ry="30" fill="green" />

直线 line <line x1="<起点X>" y1="<起点Y>" x2="<终点X>" y2="<终点Y>" stroke="<颜色>" [stroke-width] />

<line x1="10" y1="150" x2="390" y2="150" stroke="black" stroke-width="2" />

折线 polyline <polyline points="<x1>,<y1> <x2>,<y2> ..." [fill] [stroke] />

<polyline points="10,180 50,160 90,200 130,170" fill="none" stroke="purple" stroke-width="2" />

多边形 polygon <polygon points="<x1>,<y1> <x2>,<y2> ..." [fill] [stroke] />

<polygon points="200,180 240,220 160,220" fill="orange" stroke="black" />

路径 path

path 元素 <path d="<路径命令>" [fill] [stroke] [stroke-width] />

<!-- 三角形 -->
<path d="M 100 100 L 200 100 L 150 50 Z" fill="yellow" stroke="black" />

<!-- 心形 -->
<path d="M 100 200 C 50 100, 0 200, 100 300 C 200 200, 150 100, 100 200 Z" fill="red" />

路径命令

命令说明示例
M移动到(绝对)M 10 10
m移动到(相对)m 10 10
L直线到(绝对)L 100 100
l直线到(相对)l 10 10
H水平线到H 100
V垂直线到V 100
C三次贝塞尔C 20,20 40,20 50,10
S平滑三次贝塞尔S 40,20 50,10
Q二次贝塞尔Q 50,0 100,50
T平滑二次贝塞尔T 100,50
A弧线A 25,25 0 0,1 50,25
Z闭合路径Z

小写字母为相对坐标,大写字母为绝对坐标。

A 弧线参数 A rx ry x-axis-rotation large-arc-flag sweep-flag x y

<!-- 半圆弧 -->
<path d="M 50 100 A 50 50 0 0 1 150 100" stroke="red" fill="none" />

文本

text 元素 <text x="<X>" y="<Y>" [font-size] [font-family] [fill] [text-anchor] [dominant-baseline]>[文本]</text>

<text x="20" y="50" font-size="24" font-family="Arial" fill="black"
      text-anchor="start" dominant-baseline="alphabetic">
  Hello SVG
</text>
text-anchor 值对齐方式
start左对齐
middle居中
end右对齐

tspan 子文本

<text x="10" y="50">
  <tspan font-weight="bold">Hello</tspan>
  <tspan fill="red">World</tspan>
</text>

textPath 沿路径排版

<defs>
  <path id="curve" d="M 50 150 Q 200 50, 350 150" />
</defs>
<text font-size="20" fill="blue">
  <textPath href="#curve">沿曲线排列的文字</textPath>
</text>

渐变与滤镜

线性渐变

<defs>
  <linearGradient id="lg" x1="0%" y1="0%" x2="100%" y2="0%">
    <stop offset="0%" stop-color="red" />
    <stop offset="50%" stop-color="yellow" />
    <stop offset="100%" stop-color="blue" />
  </linearGradient>
</defs>
<rect x="50" y="50" width="100" height="80" fill="url(#lg)" />

径向渐变

<defs>
  <radialGradient id="rg" cx="50%" cy="50%" r="50%">
    <stop offset="0%" stop-color="white" />
    <stop offset="100%" stop-color="black" />
  </radialGradient>
</defs>
<circle cx="100" cy="100" r="80" fill="url(#rg)" />

滤镜

<defs>
  <!-- 高斯模糊 -->
  <filter id="blur">
    <feGaussianBlur stdDeviation="5" />
  </filter>

  <!-- 阴影 -->
  <filter id="shadow">
    <feDropShadow dx="4" dy="4" stdDeviation="3" flood-color="black" flood-opacity="0.5" />
  </filter>
</defs>

<rect x="50" y="50" width="100" height="80" fill="blue" filter="url(#shadow)" />

g 分组与 use 引用

g 分组 <g [transform] [fill] [stroke] [opacity]>...</g>

<g transform="translate(50, 50)" fill="red" stroke="black">
  <rect width="50" height="50" />
  <circle cx="80" cy="25" r="25" />
</g>

defs 与 use

<defs>
  <symbol id="icon" viewBox="0 0 24 24">
    <circle cx="12" cy="12" r="10" />
  </symbol>
</defs>

<!-- 多次引用 -->
<use href="#icon" x="0" y="0" width="50" height="50" />
<use href="#icon" x="60" y="0" width="50" height="50" fill="red" />

SVG 属性参考

通用样式属性

属性作用示例
fill填充颜色fill="red"
fill-opacity填充透明度fill-opacity="0.5"
stroke描边颜色stroke="black"
stroke-width描边宽度stroke-width="2"
stroke-opacity描边透明度stroke-opacity="0.8"
stroke-linecap线帽 butt/round/squarestroke-linecap="round"
stroke-linejoin连接 miter/round/bevelstroke-linejoin="round"
stroke-dasharray虚线stroke-dasharray="5,5"
opacity整体透明度opacity="0.8"
transform变换transform="rotate(45)"

transform 变换

<!-- translate/scale/rotate/skew -->
<g transform="translate(50,50) rotate(45) scale(1.5)">
  <rect width="100" height="100" />
</g>

SVG 动画

SMIL 动画(SVG 原生)

<rect x="0" y="0" width="50" height="50" fill="red">
  <animate attributeName="x" from="0" to="200" dur="2s" repeatCount="indefinite" />
</rect>

<circle cx="50" cy="50" r="20" fill="blue">
  <animate attributeName="r" values="20;40;20" dur="2s" repeatCount="indefinite" />
</circle>

CSS 动画

<style>
  .box {
    animation: move 2s infinite alternate;
  }
  @keyframes move {
    from { transform: translateX(0); }
    to { transform: translateX(100px); }
  }
</style>

<svg>
  <rect class="box" width="50" height="50" fill="red" />
</svg>

SVG 在 HTML 中的使用

直接内联

<svg width="50" height="50">
  <circle cx="25" cy="25" r="20" fill="red" />
</svg>

img 引用

<img src="icon.svg" alt="图标" width="50" height="50" />

CSS 背景

.icon {
  background: url('icon.svg') no-repeat center;
  width: 50px;
  height: 50px;
}

object 引用

<object data="icon.svg" type="image/svg+xml" width="50" height="50"></object>

动手试试

入门版(必做)

  1. 用 <circle> 和 <rect> 画一个简单的“太阳和房子”;
  2. 给房子加一个 viewBox="0 0 100 100",观察不同 preserveAspectRatio 的效果;
  3. 用 <text> 在图形下方加一行说明文字。

进阶版(选做)

  1. 用 <symbol> + <use> 做一个 3 个相同图标的工具栏,第二个改成红色;
  2. 用 <animate> 让圆点沿水平方向来回移动;
  3. 用 CSS 给内联 SVG 加悬停变色效果。

核心知识点

一句话记住 SVG:矢量图形用代码画,放大不糊;svg 是画布,g 分组,use 复用;viewBox 定坐标,path 画任意形状。

  • SVG 是矢量格式:无损缩放、可被 CSS/JS 操作,适合图标与图表;
  • viewBox 定义内部坐标系,preserveAspectRatio 控制对齐;
  • 基本形状:rect/circle/ellipse/line/polyline/polygon;
  • path 用 M/L/C/A/Z 等命令绘制任意路径;
  • defs + symbol + use 实现图标复用,g 实现分组;
  • 动画可用 SMIL <animate> 或 CSS。

注意事项与改进建议

问题点说明改进方案
图片用位图硬放放大模糊、体积大图标/Logo 改用 SVG
忘记 viewBox坐标混乱、缩放变形统一用 viewBox 定义坐标系
手写复杂 path易错且难以维护用设计工具导出,或复用图标库
<img> 引用 SVG无法用 CSS 改色需要改色时内联 SVG
滤镜滥用渲染性能下降滤镜只用于小范围图形
缺少 xmlns部分环境无法识别根 <svg> 保留 xmlns 声明

扩展学习

  • 坐标系:svg/030-SVGCoordinateSystemViewBox 深入 viewBox 与坐标变换;
  • 动画:css/330-CSSAnimationTransition 对比 CSS 动画与 SMIL;
  • 图标系统:html5/230-HTML5MultimediaCanvasDrawing 中 SVG 图标示例;
  • 绘图选型:html5/230-HTML5MultimediaCanvasDrawing 中 Canvas 与 SVG 对比。