SVG scenes
用
template svg:把 Interact controls / Computed / Timer 接到声明式 SVG 场景——画弹簧、摆、行星、电路、任何 2D 物理 / 几何动画。 比 Vega-Lite chart 更自由(任意形状),但需要你写 SVG 知识。
When to use
- ✅ 物理仿真(弹簧 / 摆 / 行星轨道 / 阻尼振动)
- ✅ 几何演示(三角函数 / 圆周运动 / 反射折射 / 几何变换)
- ✅ 电路 / 流体 / 光路示意图(动态参数)
- ✅ 自定义图标 / 数据可视化(vega 表达不出来的形状)
- ✅ 简单角色动画(机器人挥手、表情变化、玩具运动)
- ❌ 数据图表(折线 / 柱状 / 散点)→ 用 Vega-Lite,简洁很多
- ❌ 复杂 3D(SVG 是 2D;要 3D 用 WebGL / Three.js,Rho 不直接支持)
- ❌ 像素画 / 位图(用
<img>而不是 SVG)
Basic syntax
\```interact
slider angle 0 360 0 1
template svg:
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#888" stroke-width="0.5"/>
<line x1="50" y1="50"
x2="{50 + 40*cos(angle*pi/180):.2f}"
y2="{50 + 40*sin(angle*pi/180):.2f}"
stroke="red" stroke-width="2"/>
</svg>
\```
template svg: 后是 SVG markup——{var} 占位符在任何属性里都可被替换。
渲染:圆 + 一条从圆心射出的红线,拖 angle 滑块红线绕圈转。
占位符在 SVG 属性里
{expression} 可以出现在 任何 SVG 属性值里,求值后插入:
<circle cx="{50 + 40*cos(angle*pi/180):.2f}"
cy="{50 + 40*sin(angle*pi/180):.2f}"
r="{radius}"
fill="{isActive ? 'red' : 'blue'}"
opacity="{alpha:.2f}"/>
mini DSL 支持的运算(详见 Computed)—— 三角函数 / 幂 / 三元式都可在 {} 里。
安全(SVG sanitizer)
Rho 内置 SVG sanitizer,自动剥离危险元素 / 属性:
| 被剥离 | 原因 |
|---|---|
<script> |
任意 JS 执行 |
<foreignObject> |
嵌入 HTML(可能 XSS) |
on* 事件属性(onclick / onload / 等) |
内联 JS |
href 含 javascript: URL |
XSS |
<image href="external://..."> |
防外部资源跟踪 |
保留:
- 标准 SVG 形状(rect, circle, line, path, polygon, ellipse, polyline)
- 文本(text, tspan)
- 容器(g, defs, symbol, use)
- 渐变 / 模式(linearGradient, radialGradient, pattern)
- 标准属性(cx, cy, x, y, width, height, fill, stroke, transform 等)
<title>/<desc>(accessibility 友好)<animate>/<animateTransform>(标准 SVG 动画)
安全设计意味着 SVG 是 完全声明 + 不可执行 JS 的——这是 Rho 整体安全立场的延伸。
Examples
Example 1:圆周运动(pin + 滑块)
\```interact
slider angle 0 360 0 1
slider radius 10 50 40 1
template svg:
<svg viewBox="0 0 100 100" width="200" height="200">
<circle cx="50" cy="50" r="{radius}" fill="none" stroke="#888" stroke-width="0.5" stroke-dasharray="2,2"/>
<line x1="50" y1="50"
x2="{50 + radius*cos(angle*pi/180):.2f}"
y2="{50 + radius*sin(angle*pi/180):.2f}"
stroke="red" stroke-width="1.5"/>
<circle cx="{50 + radius*cos(angle*pi/180):.2f}"
cy="{50 + radius*sin(angle*pi/180):.2f}"
r="3" fill="red"/>
</svg>
\```
渲染:虚线圆 + 红线 + 红点。两个滑块同时控半径和角度。
Example 2:自动旋转(timer 配 SVG)
\```interact
timer angle 0 360 2 loop
template svg:
<svg viewBox="0 0 100 100" width="150" height="150">
<g transform="translate(50, 50)">
<circle r="40" fill="none" stroke="#888"/>
<g transform="rotate({angle:.0f})">
<line x1="0" y1="0" x2="40" y2="0" stroke="red" stroke-width="2"/>
<circle cx="40" cy="0" r="3" fill="red"/>
</g>
</g>
</svg>
\```
渲染:自动旋转的红线 + 红点(每 50ms 转 2°,全圈约 9 秒)。用 SVG <g transform="rotate(...)"> 做旋转,比 cos/sin 直接算更直观。
Example 3:弹簧振子完整仿真
\```interact
timer t 0 10 0.05 loop
slider amplitude 5 40 25 1
slider period 0.5 4 2 0.1
template svg:
<svg viewBox="0 0 200 100" width="400" height="200">
<line x1="20" y1="50" x2="180" y2="50" stroke="#ccc" stroke-dasharray="3,3"/>
<line x1="100" y1="20" x2="100" y2="80" stroke="#aaa" stroke-width="0.5"/>
<text x="100" y="15" text-anchor="middle" font-size="6" fill="#888">平衡位置</text>
<circle cx="{100 + amplitude * sin(2*pi*t/period):.2f}" cy="50" r="6" fill="red"/>
</svg>
\```
渲染:水平线(地面)+ 平衡位置标记 + 红球做简谐运动。两个滑块控振幅和周期,timer 驱动时间。
Example 4:太阳系简化模型
\```interact
timer t 0 1000 1 loop
template svg:
<svg viewBox="0 0 200 200" width="400" height="400">
<!-- 太阳 -->
<circle cx="100" cy="100" r="8" fill="orange"/>
<!-- 地球轨道 -->
<circle cx="100" cy="100" r="40" fill="none" stroke="#ccc" stroke-width="0.5" stroke-dasharray="2"/>
<circle cx="{100 + 40*cos(t*0.1):.2f}" cy="{100 + 40*sin(t*0.1):.2f}" r="4" fill="blue"/>
<!-- 火星轨道 (周期更长) -->
<circle cx="100" cy="100" r="60" fill="none" stroke="#ccc" stroke-width="0.5" stroke-dasharray="2"/>
<circle cx="{100 + 60*cos(t*0.05):.2f}" cy="{100 + 60*sin(t*0.05):.2f}" r="3" fill="red"/>
<!-- 木星轨道 (周期更长) -->
<circle cx="100" cy="100" r="80" fill="none" stroke="#ccc" stroke-width="0.5" stroke-dasharray="2"/>
<circle cx="{100 + 80*cos(t*0.02):.2f}" cy="{100 + 80*sin(t*0.02):.2f}" r="6" fill="brown"/>
</svg>
\```
渲染:太阳(橙)+ 地球(蓝)+ 火星(红)+ 木星(棕),各自轨道周期不同(地球转得最快,木星最慢)。timer 驱动所有行星同步运动。
Plain-text fallback behavior
\```interact
slider angle 0 360 0 1
template svg:
<svg viewBox="0 0 100 100">
...
</svg>
\```
→ 不支持的 reader 显示为代码块——读者看到完整 SVG markup(含 {angle} 占位符),有 SVG 知识的读者能想象画的是什么。
| Reader | 渲染效果 |
|---|---|
| Rho | 完整可交互 SVG 场景 |
| GitHub web | 代码块(SVG markup 字面量) |
| Obsidian | 同上 |
| VS Code 默认预览 | 同上 |
| cat / less | plain text |
注:GitHub 等不会自动渲染 SVG markup(即使在 markdown 里直接写
<svg>也不渲染)。Rho 的 svg template 才让它活起来。
Common pitfalls
1. SVG 语法错(缺 closing tag)
template svg:
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40"
← ❌ 缺 />
</svg>
→ SVG 解析失败,整块不渲染。SVG 是严格 XML——所有自闭合标签必须 />,所有元素必须配对 close。
推荐:在 SVG 编辑器(Inkscape / SVG-Edit / Boxy SVG)里画好导出 + 微调。
2. 占位符在错位置
template svg:
<circle cx={x} cy={y}> ← ❌ 缺引号
<circle cx="{x}" cy="{y}"> ← ✅
SVG 属性值必须双引号包——即使是替换出数字。
3. mini DSL 不支持的函数
template svg:
<rect ... transform="rotate({Math.atan2(dy, dx)})"> ← ❌ Math.atan2 不存在
← ✅ 用 atan2(dy, dx)(mini DSL 内置)
mini DSL 不是 JS——Math.X / Number.X 都不存在。直接用 Computed 函数列表。
4. 试图嵌入 HTML / script
template svg:
<svg>
<foreignObject>...</foreignObject> ← ❌ 被 sanitizer 剥离
<script>...</script> ← ❌ 被剥离
<a href="javascript:...">...</a> ← ❌ href 被清空
</svg>
→ Rho 安全策略 故意阻止 这些。如果你需要这种能力,Rho 不是合适工具。
5. viewBox 没设 → 渲染异常
template svg:
<svg width="200" height="200"> ← ⚠️ 缺 viewBox,缩放行为不可预期
<svg viewBox="0 0 200 200" width="200" height="200"> ← ✅ 推荐
总是设 viewBox——它定义内部坐标系;外部 width/height 控制实际显示尺寸;二者协作才能可缩放。
6. 性能:too many elements
template svg:
<svg>
<!-- 1000 个 circle 每帧重渲染 → 浏览器卡 -->
<circle ... />
<circle ... />
... × 1000
</svg>
→ SVG 元素数量直接影响渲染性能。< 200 元素最舒服。需要更多用 vega-lite 或 canvas(Rho 不支持后者)。
7. 动画跳变(mini DSL 表达式不连续)
template svg:
<circle cx="{floor(t*10)}" cy="50" r="3"/> ← ⚠️ 用 floor 导致每秒只跳 10 次(看着卡)
<circle cx="{t*10:.2f}" cy="50" r="3"/> ← ✅ 平滑插值
避免在表达式里用 floor / ceil / round(除非真的要离散效果)。
推荐 SVG 学习入口
如果你不熟 SVG:
- MDN SVG 教程
- SVG-Edit 在线编辑器 — 画完导出 markup
- Boxy SVG — 桌面 SVG 编辑器
熟练后回来,本页就够你做出 90% 的 Rho 动画。
See also
- Interact controls — slider 等控件驱动 SVG
- Timer — 自动播放 SVG 动画
- Computed — mini DSL 函数完整列表
- Interact + Vega-Lite chart — 标准图表(不是 SVG)
- Shared state (namespace)
- Plain-text fallback principle