ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SVG完全指南:从基础语法到动画、交互与工程化实战

SVG完全指南:从基础语法到动画、交互与工程化实战 后台收到不少留言问我SVG 到底有什么好说实话作为一个写了好几年前端、平时也免不了画图做方案的开发者SVG 是我最常用又最容易被低估的工具。很多人一听到“SVG 图片”脑子里就是“不就是矢量图嘛放大不糊”。但真正用起来SVG 远不止是“不糊的位图”。它本质是一段可编程的 XML 文本放到浏览器里会被解析成 DOM 节点可以直接用 CSS 控制样式、用 JavaScript 操作结构、用 SMIL 做动画甚至在服务端生成、在移动端转成 Kotlin 的 ImageVector。这篇文章就把 SVG 的语法、代码、实战场景一次性理清楚尽量让新手看完能上手老手也能找到几个平时容易忽略的点。1. 先想清楚SVG 到底是什么凭什么比位图强1.1 剥开 SVG 的外壳一段有结构的文本SVG 的全称是 Scalable Vector Graphics可缩放矢量图形。标准由 W3C 维护和 HTML、CSS 同属一个生态体系。它的核心思想不是记录“每个像素的颜色”而是记录“图形的几何描述”这个圆圆心在哪、半径多少这条线从哪个坐标画到哪个坐标这块区域填充什么颜色。所以一张 SVG 图片本质上就是一个 XML 文档。比如一个最简单的红色圆svg width100 height100 xmlnshttp://www.w3.org/2000/svg circle cx50 cy50 r40 fillred / /svg这段代码放到任意支持 SVG 的浏览器里就能渲染出一个红色圆。也正因为它是文本而不是二进制SVG 可以被人直接阅读和修改可以被版本管理工具追踪 diff可以被脚本动态生成。这一点是 PNG/JPG 永远做不到的你没法用一个正则去改 JPG 里的某个圆但你可以很轻松地改 SVG 里的 circle 标签。这个特性带来几个直接好处SEO 友好内容可以被搜索引擎读取、压缩率高gzip 之后文本体积很小、无障碍友好可以加 title 和 desc 标签描述内容。很多图标字体、地图标注、数据图表背后其实都是 SVG 在支撑。1.2 选型对比SVG 与 Canvas、PNG 的分工很多人上来就问“SVG 和 Canvas 哪个好”这个问题本身就有问题。它俩不是替代关系而是拿着不同工具的工种。Canvas 是画布式渲染你拿到一块矩形区域用 JavaScript 在上面画点、线、面画完就画完了内部不会保留图形对象。它适合高频率重绘的场景比如游戏、粒子特效、视频处理几千上万帧画面要跑满 60fpsCanvas 的“一次性绘制”模型有明显优势。代价是“无状态”你想让某个刚才画过的圆变颜色你得重绘整个画面哪怕只改了一个元素。SVG 是保留型的 DOM 结构每个图形都是一个节点你能随时拿到它、改它、删除它浏览器会自动帮你重绘受影响区域。它天然适合“图形数量中等但交互性强”的场景鼠标 hover 变色、点击选中等逻辑写起来非常直白不用自己维护一份数据再整体重绘。再看 PNG/JPG 这类位图优势是照片级表现了细腻的渐变、真实的噪点、复杂的纹理这些用 SVG 表达成本极高。但是位图放大到超过原始尺寸就会模糊而且体积随尺寸线性增长。一张 1920×1080 的截图可能几 MB而同样的 UI 界面用 SVG 描述可能只要几十 KB。我的建议是直接看场景选型不需要纠结谁强谁弱需求推荐方案原因图标、Logo、简单插画SVG体积小、清晰度高、可配色可动画数据图表、图表交互SVG节点可绑定事件、易于局部更新地图与导览系统SVG区域可划分、可点击、可缩照片、复杂纹理位图渲染 fidelity 和编写成本综合考虑大规模粒子、游戏Canvas/WebGL重绘性能2. 手写第一张 SVG基础语法与坐标体系2.1 坐标系与 viewBox理解“缩放”的本质SVG 的坐标系和数学里的平面直角坐标系长得很像但注意一个区别y 轴方向是向下的。左上角是 (0,0)往右 x 增大往下 y 增大。这点和 Canvas、DOM 的坐标体系一致但和很多人中学数学里的坐标系相反初次接触的人容易把圆画到了屏幕外面这不是 bug是坐标系方向问题。viewBox 是 SVG 里最重要的属性之一它的格式是四个数字viewBoxminX minY width height前两个数字是可视区域左上角的逻辑坐标后两个数字是可视区域的逻辑宽高。配合 width 和 height 属性viewBox 决定的是“怎么把逻辑坐标映射到实际渲染尺寸”。我用一个例子说明。下面这个 SVG 实际尺寸是 200×200但 viewBox 是 0 0 100 100意味着逻辑上 100 个单位的图形会被拉大到 200 像素宽svg width200 height200 viewBox0 0 100 100 xmlnshttp://www.w3.org/2000/svg circle cx50 cy50 r40 fill#4A90D9 / /svg这时候你改变 width 和 height图形会跟着等比伸缩但内容比例不变。这就是 SVG “响应式”的基础。反过来如果你不写 viewBox 只写 width/height那 width/height 就是画布的真实大小里面的坐标直接按像素算放大时就会失真——虽然 SVG 是矢量格式但如果你用绝对像素硬撑还是会糊。实战经验做图标的时候我习惯统一设viewBox0 0 24 24或0 0 48 48这样放到不同尺寸环境下只需要设置 width 和 height里面的坐标不用动。团队协作时也统一一个 scope避免一人一套坐标。2.2 基础图元速查从矩形到多边形SVG 提供了很多开箱即用的基本图形标签我把高频的几个列出来每个配一个最小可运行示例。矩形 rectrect x10 y10 width80 height50 rx8 fill#F5A623 /x/y 是左上角坐标width/height 是宽高rx/ry 是圆角半径。做按钮背景、卡片圆角底的时候非常方便。圆形 circlecircle cx50 cy50 r30 fill#7ED321 /cx/cy 是圆心坐标r 是半径。做头像、状态点、气泡坐标点都用它。椭圆 ellipseellipse cx50 cy50 rx40 ry20 fill#9013FE /rx 是水平半径ry 是垂直半径。比 circle 多了一个轴上的控制。线条 lineline x10 y10 x2100 y280 stroke#000 stroke-width2 /注意线没有 fill只有 strokestroke-width 定义线宽。很多新手只写 stroke 忘写 stroke-width结果线粗得离谱或者细到看不见。折线 polylinepolyline points0,0 50,20 100,80 150,40 fillnone stroke#34B8D1 /points 是一串坐标对能画折线图、走势图。多边形 polygonpolygon points50,10 90,90 10,90 fill#F8E71C stroke#333 stroke-width1 /polygon 和 polyline 的区别在于 polygon 会自动闭合最后一条边适合画三角形、六边形、不规则区域。组标签 g 很重要g fillnone stroke#333 circle cx30 cy30 r20 / circle cx70 cy30 r20 / /gg 的作用是分组对一组元素统一设置样式、统一做 transform 变换还能配合 defs 做复用。想对一个区块里的所有子元素做位移、旋转直接对 g 下手别一个元素一个元素地改。2.3 path 路径SVG 的“万能画笔”前几个标签都是快捷方式真正让 SVG 表达力达到顶峰的是 path。它用一系列命令描述画笔轨迹移动到某个点、画直线、画曲线、画弧线最后闭合。只要你能想象出来的图形理论上 path 都能画。path 的 d 属性是关键里面是一连串命令加坐标M 10 10 // move to把画笔移动到 (10,10) L 50 10 // line to画直线到 (50,10) C 70 10 90 50 100 80 // cubic bezier 三次贝塞尔曲线 A 20 20 0 0 1 120 60 // arc 圆弧 Z // close path闭合当前路径回到起点大小写有严格含义大写命令是“绝对坐标”小写命令是“相对坐标”。比如l 10 0表示相对当前点向右移动 10 个单位。做动画和动态生成的时候相对坐标有时候更好用因为你不用关心起点在哪只要关心偏移量。一个常见的心形路径示例path dM 10,30 A 20,20 0 0,1 50,30 A 20,20 0 0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 Z fill#FF6B6B /这里 A 画圆弧Q 画二次贝塞尔曲线配合 Z 闭合。虽然看起来像咒语但拆开看就是“左半沿圆弧上来再沿圆弧下去中间用两条贝塞尔曲线收尾”。理解了每个字母的含义path 就不再神秘。我的建议是不是所有图形都要硬写 path能用 rect/circle 用的就别自找麻烦。path 适合的是图标里那些不规则轮廓、字体图标转换、地图区块轮廓这些场景。平时做图直接去 Figma 或 Illustrator 拖出来然后复制 SVG 路径比手写快得多。3. 让图形活起来动画与交互代码实操3.1 CSS 动画最省力的动效方案SVG 节点是 DOM 的一部分所以 CSS 可以作用于 SVG 元素。这意味着你不需要引入任何动画库直接用 CSS 就能做很多效果。举个例子做一个加载转圈的图标svg viewBox0 0 100 100 classspinner circle cx50 cy50 r40 fillnone stroke#ccc stroke-width8 / circle cx50 cy50 r40 fillnone stroke#333 stroke-width8 stroke-dasharray251 stroke-dashoffset251 classspinner-circle / /svg.spinner { width: 60px; height: 60px; animation: spin 1.2s linear infinite; } .spinner-circle { animation: dash 1.2s ease-in-out infinite; } keyframes spin { to { transform: rotate(360deg); } } keyframes dash { 0% { stroke-dashoffset: 251; } 50% { stroke-dashoffset: 62.5; transform: rotate(135deg); } 100% { stroke-dashoffset: 251; transform: rotate(450deg); } }这里的核心技巧是 stroke-dasharray 和 stroke-dashoffset 组合。stroke-dasharray 把描边切成一段实线和一段空白stroke-dashoffset 控制这一段的起始位置偏移。把这个原理用在 path 上就能实现流行的“描边生长动画”先算出路径总长度然后让 dashoffset 从路径长度逐渐变到 0看起来就像画笔在逐渐画出轮廓。获取路径长度的代码很简洁const path document.querySelector(#somePath); const length path.getTotalLength(); console.log(length);用这个 length 值去设置 stroke-dasharray 和 stroke-dashoffset 就能精准控制动画进度。这个技巧在页面加载动画、Logo 出场效果、导览系统里高亮某条路线时特别实用。3.2 SMIL 动画纯 SVG 里的时间轴CSS 动画虽然强但有个天然短板它更适合做“循环重复”的效果对于“按时间顺序执行一系列动作并且互相联动”稍微麻烦。SVG 原生自带一套 SMIL 动画标签可以在 SVG 内部定义一个完整的时间轴。最常用的是 animate 和 animateTransformsvg viewBox0 0 100 100 xmlnshttp://www.w3.org/2000/svg rect x10 y10 width20 height20 fill#4A90D9 animate attributeNamex values10; 70; 10 dur3s repeatCountindefinite / /rect /svgattributeName 指定要变化的属性values 是动画关键帧的取值序列dur 是周期时长repeatCount 是重复次数。这个方块会在 3 秒内从 x10 移动到 x70 再回到 10循环往复。如果是旋转、缩放、平移这类 transform 变化要使用 animateTransformcircle cx50 cy50 r15 fill#FF6B6B animateTransform attributeNametransform typerotate from0 50 50 to360 50 50 dur2s repeatCountindefinite / /circlefrom 和 to 里rotate 的语法是“角度 圆心x 圆心y”表示绕哪个点转。这里表示绕 (50,50) 这个点旋转 360 度。SMIL 对事件支持也不错可以配合 beginclick 让动画在用户点击时启动。现在主流浏览器支持情况已经很好除了一些老掉牙的移动端浏览器内核基本放心用。3.3 JS 交互让 SVG 响应点击与数据SVG 节点就是 DOM 节点你可以用 querySelector 或者事件委托绑定点击、悬浮、拖拽。一个很常见的场景点击某个图形区块把它高亮同时让其他区块变灰。比如一个简单的国家地图分布g idregion-north classregion>const regions document.querySelectorAll(.region); regions.forEach(region { region.addEventListener(click, function () { regions.forEach(r r.classList.remove(active)); this.classList.add(active); }); });.region { fill: #ccc; cursor: pointer; transition: fill 0.2s; } .region:hover { fill: #a0c4ff; } .region.active { fill: #4A90D9; }因为 SVG 是 DOMCSS 的伪类 hover、active、transition 都能直接用不用额外写 mouseenter/mouseleave。这也是我在交互场景里优先选 SVG 而不是 Canvas 的最重要原因之一省了非常多事件管理代码。数据驱动场景同样方便。比如后端返回一组坐标点你动态生成 circle 塞进 SVGconst points [ { x: 20, y: 30, value: 50 }, { x: 60, y: 50, value: 80 }, { x: 90, y: 20, value: 30 } ]; const svg document.querySelector(#chart); points.forEach(p { const circle document.createElementNS(http://www.w3.org/2000/svg, circle); circle.setAttribute(cx, p.x); circle.setAttribute(cy, p.y); circle.setAttribute(r, p.value / 10); circle.setAttribute(class, data-point); svg.appendChild(circle); });注意这里用的是 document.createElementNS 而不是 document.createElement。普通 createElement 创建出来的是 HTML 元素在 SVG 命名空间里渲染不出来。这是新手最容易踩的坑之一。3.4 进阶动画风格与 Live2D 的参照有时候看到“svg live2d”这种关键词会误以为 SVG 能直接做 Live2D 那种立体可动的角色。严格来说 Live2D 是纹理网格变形技术和 SVG 路线不同。但 SVG 完全可以通过对多个图层分别变形、位移、旋转实现类似“关节带动”的伪 2D 动画效果头部一个 g躯干一个 g四肢各自一个 g用 JS 或者 SMIL 让它们分别动。这样做低精度、扁平风格的“轻量 Live2D”在网页上完全可行而且文件比视频和位图序列小得多。4. 场景实战从图标系统到室内导览4.1 图标系统告别图片雪碧图的年代早年间做网页图标用 CSS Sprite把所有图标拼到一张 PNG 上再用 background-position 定位。问题是切图麻烦、换色麻烦放大还糊。后来图标字体流行过一阵比如 Font Awesome但字体图标在渲染细节、多色图标上天生受限。SVG 图标系统把这些痛点基本全解决了。最基础的用法是 SVG Sprite把所有图标合成一个 SVG 文件每个图标用 symbol 包裹配合 use 引用。svg xmlnshttp://www.w3.org/2000/svg styledisplay:none symbol idicon-home viewBox0 0 24 24 path dM12 3 L22 12 L19 12 L19 21 L14 21 L14 14 L10 14 L10 21 L5 21 L5 12 L2 12 Z / /symbol symbol idicon-user viewBox0 0 24 24 path d... / /symbol /svg使用时svg classicon width24 height24 use href#icon-home / /svg视觉上是正常的 24×24 图标底层还是一片可引用资源。改颜色只需要写一条 CSS.icon { fill: currentColor; }这样图标的颜色自动跟随文字颜色主题切换的时候一套代码全变。整个 Sprite 文件可以统一做 buffer 缓存加载一次到处使用。现在很多组件库的图标方案底层也是这一套。在实际工作中我更推荐在构建阶段把 SVG 图标直接编译成组件而不是运行时 use因为运行时引用会多一次网络依赖。但小型项目或者非工程化页面SVG Sprite 方案依然是性价比最高的。4.2 数据可视化图表里的 SVG 身影只要打开过 ECharts、D3.js 的渲染面板你就会发现 SVG 在其中占了大半壁江山。折线图、柱状图、热力图、地图只要图形数量在几千以内SVG 都能舒服地拿下。为什么可视化库偏爱 SVG原因是交互和状态更新图表里最常见的操作是移动端横滑数据点、鼠标悬浮显示 tooltip、点击某个柱子下钻。这些操作本质上是“把某个节点的样式改一改顺便在附近插入一个提示框”。DOM 模型做这件事就是天生顺手CSS 可以控制样式事件能直接绑定到元素上不需要去 Canvas 上做像素级别的命中检测。D3.js 的核心哲学也是“数据驱动 DOM”你把数据映射成 SVG 属性剩下的交给浏览器渲染。比如画一个最简单的柱状图数据数组 [4, 8, 10] 映射成三个 rectconst data [4, 8, 10]; const chart d3.select(#chart); chart.selectAll(rect) .data(data) .enter() .append(rect) .attr(x, (d, i) i * 40) .attr(y, d 100 - d * 8) .attr(width, 30) .attr(height, d d * 8) .attr(fill, #4A90D9);这段代码语义清晰页面上有几个数据就画几个矩形位置和高度由数据算出来。数据更新时enter、update、exit 三段逻辑分别处理新增、更新、删除。这比手动管一堆 Canvas 绘制指令简单得多。另外很多量化分析、Python 数据项目里提到的 “python量化交易策略代码” 通常也只是产出数据真正要把策略图展示成网页报告还是会把数据转成 SVG 结构的图表。包括 XGBoost、LSTM 模型的训练结果分析、特征重要度可视化最终落到前端也是 SVG 图表居多。所以做算法的人建议也了解一下 SVG 的结构否则调报表样式时无从下手。4.3 室内导览与地图标注SVG 的隐藏主场“svg室内导览系统”这个关键词背后是一个特别典型的 SVG 应用场景商场导航、展览馆导览、停车场找车位。为什么这类系统特别适合 SVG第一个理由是区域划分。室内地图的每个商铺、每个展位都是不规则多边形在 SVG 里就是一个 polygon 或者 path。给每个区域加一个>g idmap-layer polygon points...>// 监听鼠标滚轮缩放 map-layer 的 scale 值 const layer document.querySelector(#map-layer); let scale 1; map.addEventListener(wheel, e { scale e.deltaY 0 ? -0.05 : 0.05; scale Math.max(0.5, Math.min(3, scale)); layer.setAttribute(transform, scale(${scale})); });缩放时如果用 scale 直接作用于整个地图层注意点击事件坐标的地图映射。如果你同时用 CSS transform 作过渡还要小心事件区域漂移问题。比较稳妥的做法是记录当前视图中心缩放时以鼠标位置为锚点调整平移量。细节不少但整体代码量相比 Canvas 方案还是小了一个量级。第三个理由是路线高亮。导览系统经常要画“从 A 到 B 的最优路线”用 SVG 完全是动画主场把路线画成 path然后用之前说的 stroke-dasharray 做流动描边视觉上是路线“生长”出来的非常直观。4.4 一个完整案例鹈鹕骑自行车动画怎么拆我见过有人聊“generate an svg of a pelican riding a bicycle”听起来像段子但它其实是一个很好的 SVG 动画教学练手题目。我们把它拆开看鹈鹕、自行车、轮子转动、脚蹬踩踏这些都是可以拆成独立 g 层的动效组件。先画自行车车架可以用几个 line轮子用 circle注意给两个轮子分别设 id转动的时候让它们绕自身圆心旋转circle idwheel-front cx150 cy180 r40 fillnone stroke#333 stroke-width4 / circle idwheel-back cx50 cy180 r40 fillnone stroke#333 stroke-width4 /轮子转动动画用 SMIL 的 animateTransform把圆心坐标写进去让轮子持续旋转animateTransform xlink:href#wheel-front attributeNametransform typerotate from0 150 180 to360 150 180 dur1s repeatCountindefinite /然后画鹈鹕身体用椭圆嘴巴用长长的 path翅膀用一个 g 包住让翅膀做小幅度的上下摆动。脚蹬可以用一个旋转的 group随着轮子转动产生联动。整体控制好每个部件的 transform-origin动画就能自然起来。这个小项目做完你会发现 SVG 动画的核心根本不是背 API而是拆层把复杂动作拆成多个 g每个 g 负责一个独立的运动再用时间轴把它们串起来。这个思路不仅适用于鹈鹕自行车任何角色动画都可以照搬。另外这种“svg提示词”类需求通常是想借助 AI 直接生成 SVG 代码但 AI 生成的 SVG 普遍存在一个问题所有节点都堆在一个 g 里线条路径也经常冗余后续想单独做某个部件动画非常困难。所以我拿到 AI 生成的 SVG 第一件事就是“拆”分析结构把能动的部件单独拎出来加上 id再写动画。不要指望一口气生成完美动画。5. 性能优化与踩坑实录5.1 渲染性能别把 SVG 当普通标签疯狂刷SVG 是 DOM所以它继承 DOM 的性能特征节点多了操作多了一样会卡。一个 2000 个 circle 的 SVG反复修改坐标重排重绘成本不低。如果单帧要更新几百个节点任督二脉就堵了。几个实用的优化手段减少 DOM 节点数量。能用 path 合并的图形不要拆成满屏 rect/circle。一个 path 有几十个指令依然是一个节点渲染压力远小于 50 个 circle 组成的效果。图标导出时尽量开启“合并路径”之类的优化选项。低频更新尽量不要操作样式属性直接改 class。比如高亮某个区域设置 fill 属性改颜色只是轻量操作但如果同时改多个属性改 class 让浏览器一次应用样式更高效率。高频更新尽量减小重绘区域。SVG 里如果只动一个小部件把它单独放到一个最底层的 g 里避免和其他静态大区域搅在一起某些性能敏感的浏览器能减少重新栅格化的影响。给初始 hidden 的复杂 SVG 设置 display:none 而不是 visibility:hidden。display:none 时浏览器不参与布局和渲染visibility 还会保留渲染空间。大量初始化场景优先用 display:none。5.2 兼容性与工程化问题SVG 的兼容历史比较曲折早期 IE 对 SVG 支持极差连基本的 viewBox 都有 bug。现在的现代浏览器基本完全支持但如果你还维护着老系统有几个点还是要留意老 Edge 和低版本 Android WebView 对部分滤镜 filter 效果支持不一致特别是 feDropShadow、feGaussianBlur 这类效果渲染可能有偏差。遇到这类场景我建议加一个 fallback先检测是否支持滤镜不支持就退化为实色边框别让页面显示成一团黑。第二是外部引用资源跨域问题。如果你在 SVG 里使用image href另一个域的图片或者 CSS 里引用了外部字体浏览器默认有很多安全限制。不是所有系统都有心思处理跨域所以团队内部约定SVG 里尽量内联资源或者走同域 CDN。第三是字体图标替代后遗留问题。老项目从字体图标迁移到 SVG 时经常有人遗漏标题、aria-label 之类的可访问性属性导致屏幕阅读器读取时没有语义。SVG 图标建议统一加title复杂图形再加desc。这块不只是“体验”项目过审或无障碍检查时也是硬指标。5.3 工具链怎么调试、怎么导出、怎么转格式调试 SVG 最直接的方式是用浏览器 DevTools 的 Elements 面板。你可以像查看普通 DOM 一样查看 SVG 结构、临时修改属性、断点调试事件。Chrome 的 Styles 面板还能直接调整 fill、stroke 等 SVG 属性并在页面实时生效。这比写死代码再刷新页面调试效率高太多。导出方面Figma、Sketch、Illustrator 都支持直接复制 SVG 代码但复制出来的代码往往带了一堆冗余的元数据和分组嵌套。我习惯先用 SVGO 做压缩去除 XML 声明、合并路径、精简坐标。命令行可以这样使用npx svgo -f ./svgs -o ./min在实际项目中建议把 SVGO 直接集成到构建流程里对所有导入的 svg 文件自动压缩。团队里经常有人把手画的十几个路径忘压了几 MB 的 SVG 就这样进了仓库页面加载时长肉眼可见地增加。至于格式转换“svg → compose imagevector kotlin”这个需求在 Android 端很常见。官方工具 Android Studio 自带 Vector Asset 支持导入 SVG 并自动生成 ImageVector 的 Kotlin 代码。操作路径右键 res 目录New - Vector Asset选择本地 SVG 文件Android Studio 会转换出对应的 xml 资源。这样你在 Android 里就能享受到 SVG 的缩放不糊、可以动态改变颜色这些好特性。另一个实用技巧是 SVG 转 base64 内联到 CSS 或 HTML 里。小图标几 KB 以内不想多一次网络请求的话直接转成 data URI 塞进 CSS 背景图.icon { background-image: url(data:image/svgxml,%3Csvg xmlns... viewBox0 0 24 24%3E%3Cpath d.../%3E%3C/svg%3E); }注意 URL 里需要把特殊字符转码比如对应%3C对应%3E空格可以用%20。这种内联方式的优势是小、快、不存在请求阻塞劣势是难以复用。只适合那种只用一次的小图标。5.4 常见问题速查表我这几年经手了不少项目把高频踩的坑归一下做成速查表给你省点时间现象可能原因解决方案代码写了但浏览器渲染空白少了 xmlns 命名空间根 svg 加上xmlnshttp://www.w3.org/2000/svgJS 创建图形不显示用了 createElement 而非 createElementNS改用document.createElementNS(http://www.w3.org/2000/svg, circle)放大图变模糊没写 viewBox直接按像素硬撑设置 viewBox让坐标和实际渲染尺寸分离动画转得位置不对transform 中心点不对给 animateTransform 的 rotate/scale 明确圆心坐标描边动画不流畅没设置 stroke-dasharray 或者数值比路径长度小先用 getTotalLength() 拿到总长度再设 array 和 offsethover 无效元素 fill 被上级 g 覆盖明确给目标元素的指定样式或提升 CSS 优先级在老手机WebView渲染错乱部分 SMIL 动画不支持平滑降级为 CSS 动画或 JS 动画方案SVG 文件超大路径未压缩、节点冗余用 SVGO 压缩开启资源合并这里面比较常见的是第一和第二个SVG 的命名空间问题。很多人一上来就直接写createElement(circle)然后浏览器怎么都不显示卡了半天。实际上 SVG 元素和 HTML 元素分属两个命名空间写错一个地方后面全部白搭。任何要动态创建 SVG 图形的代码记得命名空间字符串写对。第三个 viewBox 的问题平时不太容易暴露直到你把一张小图标放大做 banner 背景才发现边缘全糊。这不是 SVG 的锅是你没把坐标系和真实尺寸解耦。加一个 viewBox 就能解决。说实话把这些坑踩过一遍之后SVG 在我这里基本就是“表象复杂、内核简单”的工具语法标签就那么十来个真正的难点是理解坐标系和路径描述。只要你把这两块啃下来剩下的都是平时用得多手就熟的问题。如果你正在学或者正在做我的建议是给自己准备一个小的“SVG 工具箱”SVGO 做压缩、一个能预览 SVG 的本地 HTML 模板、自己积累的常用 path 片段。遇到新需求优先在现有片段上改不要每次从零画。长期做下来效率能上去不少。这个工具谁用谁知道。
返回列表