动态 SVG 模式规范
本文档是默认输出模式的规范,支持 SMIL 动画效果。
模式特性
- 动态 SVG 效果:SMIL 动画标签
- Emoji 与动态效果结合
- 视觉复杂度提升:3-6 个视觉元素
- SVG 代码直接嵌入 Markdown 文件
- 公众号完美支持(原生 SMIL)
- ⚠️ 背景色强制要求:SVG 画布必须设置非白色背景色(见下方详述)
一、SMIL 动画基础
公众号仅支持 SMIL 动画(<animate>、<animateTransform>),禁止使用 CSS @keyframes 和 JavaScript。
| 标签 | 功能 | 应用场景 |
|---|---|---|
<animate> |
属性动画 | 颜色、透明度、线条偏移 |
<animateTransform> |
变换动画 | 位移、旋转、缩放 |
二、⚠️ 背景色强制要求
【前置要求 - 最高优先级】
所有动态 SVG 画布 必须设置非白色背景色,禁止使用纯白 #FFFFFF 作为画布背景。
原因:
- 纯白背景导致圆角、边框等视觉元素与背景融为一体,无法区分
- 白色背景与内容对比度不足,视觉层次感差
- 无法体现设计感和专业性
背景色选择:
<!-- ❌ 错误:纯白背景 -->
<svg viewBox="0 0 800 450">
<rect x="60" y="60" width="680" height="330" rx="12" fill="#FFFFFF"/>
</svg>
<!-- ✅ 正确:浅色背景 -->
<svg viewBox="0 0 800 450">
<rect x="0" y="0" width="800" height="450" fill="#F0F4F8"/>
<!-- 圆角框元素 rx="10" -->
<rect x="60" y="60" width="680" height="330" rx="10" fill="rgba(255,255,255,0.8)" stroke="#D0D8E0"/>
</svg>同一篇文章内的每张配图按序号轮流使用不同背景色。背景色从以下色库选择:
#F5F5F5#F8F9FA#FAF8F5#FAF0E6#F0F4F8#E8F5E8#FFF0F5#F3E8F8#E0F0F8#FFFBE6
三、核心动态效果
效果 1:浮动动画
多角色元素上下浮动
<circle cx="200" cy="200" r="50" fill="#4A90E2">
<animateTransform attributeName="transform" type="translate"
values="0,0; 0,-10; 0,0" dur="3s" repeatCount="indefinite"
calcMode="spline" keySplines="0.4 0 0.2 1; 0.4 0 0.2 1"/>
</circle>参数:
- 浮动幅度:8-15px
- 浮动周期:2-4 秒
- 多角色错峰:0.5-1 秒
效果 2:虚线框流动
强调框架边界
<rect x="100" y="100" width="600" height="250" fill="none"
stroke="#4A90E2" stroke-width="3" rx="10" stroke-dasharray="10,5">
<animate attributeName="stroke-dashoffset" from="30" to="0"
dur="1s" repeatCount="indefinite"/>
</rect>参数:
- 虚线
10,5 - 流动 0.8-1.5 秒
效果 3:箭头绘制
展示流程指向
<defs>
<marker id="arrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#4A90E2"/>
</marker>
</defs>
<line x1="200" y1="200" x2="600" y2="200" stroke="#4A90E2"
stroke-width="4" marker-end="url(#arrow)"
stroke-dasharray="400" stroke-dashoffset="400">
<animate attributeName="stroke-dashoffset" from="400" to="0"
dur="1.5s" repeatCount="indefinite"/>
</line>参数:
- 线条粗 3-5px
- 绘制 1-2 秒
三、Emoji 动态效果
Emoji 浮动动画
<!-- ✅ 正确:用 x/y 定位,内层 g 做浮动动画 -->
<g>
<text x="200" y="225" font-size="100" text-anchor="middle">😰</text>
<g>
<animateTransform attributeName="transform" type="translate"
values="0,0; 0,-12; 0,0" dur="3s" repeatCount="indefinite"
calcMode="spline" keySplines="0.4 0 0.2 1; 0.4 0 0.2 1"/>
</g>
</g>Emoji 脉冲动画
<!-- ✅ 正确:translate 定位 + scale 动画(scale 不覆盖 translate) -->
<g transform="translate(400, 225)">
<text x="0" y="35" font-size="100" text-anchor="middle">🎯</text>
<animateTransform attributeName="transform" type="scale"
values="1; 1.08; 1" dur="2s" repeatCount="indefinite"
calcMode="spline" keySplines="0.4 0 0.2 1; 0.4 0 0.2 1"/>
</g>Emoji + 几何图形组合
<!-- ✅ 正确:translate 定位 + opacity 动画(不冲突) -->
<g transform="translate(400, 225)">
<circle cx="0" cy="0" r="90" fill="#E8F4F8">
<animate attributeName="fill-opacity" values="0.6; 1; 0.6"
dur="3s" repeatCount="indefinite"/>
</circle>
<text x="0" y="35" font-size="100" text-anchor="middle">🚀</text>
</g>四、动态效果使用原则
逻辑性动态效果优先
最重要:动态效果必须服务于逻辑关系的表达,而非单纯的装饰。
优先级表:
| 优先级 | 动态效果类型 | 作用 | 必须使用场景 |
|---|---|---|---|
| 最高 | 箭头绘制动画 | 展示指向、流程、因果关系 | 所有包含箭头/连接线的图 |
| 最高 | 虚线框流动动画 | 强调框架、边界、范围 | 所有包含虚线框的图 |
| 高 | 线条流动动画 | 展示数据/信息流动 | 流程图、关系图 |
| 中 | 脉冲动画 | 强调核心元素 | 中心概念、关键节点 |
| 低 | 浮动动画 | 增加生动感 | emoji、角色元素 |
严格规则
- 有箭头必须动画、有虚线框必须动画
- 逻辑先于装饰:先确保逻辑关系动画,再考虑 emoji 浮动
- 禁止静态箭头
推荐使用场景
多角色场景(浮动)、强调框架(虚线流动)、流程指向(箭头绘制)、核心元素(脉冲)、状态变化(颜色渐变)
谨慎使用
单元素场景、信息密集、需要稳定感的内容
组合原则
- 主次分明:1-2 种主要动画
- 节奏协调
- 方向一致
- 错峰展示
五、成功标准
- 布局多样化,动态效果自然流畅
- 公众号显示正常,动画无卡顿
- 在丰富性和可读性之间保持平衡
- 逻辑性动画优先于装饰性动画
六、⚠️ 兼容性警告
🚨 禁止 transform + animateTransform translate 组合
这是最常见的问题! 当外层有 transform="translate(x,y)",内层又有 <animateTransform type="translate"> 时,动画会完全覆盖外层的定位,导致元素飞到左上角 (0,0)。
<!-- ❌ 错误:translate 定位 + translate 动画 = 元素堆到左上角 -->
<g transform="translate(180, 200)">
<circle cx="0" cy="0" r="80" fill="#4A90E2"/>
<animateTransform type="translate" values="0,0; 0,-12; 0,0"/>
</g>
<!-- ✅ 正确方法1:直接用 cx/cy 定位,内层包一层做动画 -->
<g>
<circle cx="180" cy="200" r="80" fill="#4A90E2"/>
<g>
<animateTransform type="translate" values="0,0; 0,-12; 0,0" dur="3s" repeatCount="indefinite"/>
</g>
</g>
<!-- ✅ 正确方法2:translate 定位 + scale 动画(scale不会覆盖translate) -->
<g transform="translate(180, 200)">
<circle cx="0" cy="0" r="80" fill="#4A90E2"/>
<animateTransform type="scale" values="1; 1.1; 1" dur="2s" repeatCount="indefinite"/>
</g>
<!-- ✅ 正确方法3:纯 emoji 浮动,用 x/y 定位 -->
<text x="180" y="235" font-size="80" text-anchor="middle">🐎</text>
<g>
<animateTransform type="translate" values="0,0; 0,-12; 0,0" dur="3s" repeatCount="indefinite"/>
</g>核心原则:
transform="translate()"只能和type="scale"动画组合- 要做
type="translate"浮动动画,必须直接用cx/cy或x/y定位 - 永远不要嵌套两层
translate(一个静态一个动态)
禁止使用 SVG Filter
微信环境不兼容:使用 <g filter="url(#shadow)"> 会导致 SMIL 动画无法在微信中显示。
<!-- ❌ 错误:filter 会导致动画失效 -->
<g filter="url(#shadow)">
<rect ...>
<animateTransform attributeName="transform" .../>
</rect>
</g>
<!-- ✅ 正确:直接使用 transform 定位 -->
<g transform="translate(100, 120)">
<rect ...>
<animateTransform attributeName="transform" .../>
</rect>
</g>解决方案:
- 禁止在任何 SVG 元素上使用
filter属性 - 使用
transform替代 filter 进行视觉定位 - 阴影效果可以用边框颜色深浅或背景色区分来替代
禁止渐变填充 + 动画组合
微信环境不兼容:当 fill="url(#gradient)" 和 <animateTransform> 在同一个元素上时,渐变无法显示。
<!-- ❌ 错误:渐变和动画在同一元素上 -->
<rect fill="url(#gradient)">
<animateTransform attributeName="transform" .../>
</rect>
<!-- ✅ 推荐:直接使用纯色 -->
<rect fill="#10B981">
<animateTransform attributeName="transform" .../>
</rect>🚫 禁止所有渐变填充
公众号不支持所有渐变:即使静态的渐变背景也无法渲染,请使用纯色。
<!-- ❌ 错误:渐变背景 -->
<linearGradient id="bgGrad">...</linearGradient>
<rect fill="url(#bgGrad)"/>
<!-- ✅ 正确:使用纯色 -->
<rect fill="#f8f9fa"/>七、✅ 动画组合速查表
| 定位方式 | 可用动画类型 | 示例 |
|---|---|---|
transform="translate()" |
type="scale" |
✅ 脉冲缩放 |
transform="translate()" |
type="rotate" |
✅ 旋转 |
transform="translate()" |
type="translate" |
❌ 禁止!会覆盖定位 |
cx/cy 或 x/y 直接定位 |
type="translate" |
✅ 浮动动画 |
| 任意定位 | <animate> 属性动画 |
✅ 颜色、透明度等 |
记忆口诀:
translate 定位只能配 scale/rotate,要做浮动必须用 x/y/cx/cy 直接定位!