SVG 变换 transform
translate、rotate、scale、skew、matrix 与 transform-origin 变换组合。
前置知识:用户坐标系与 viewBox(《SVG 坐标系统与 viewBox》,003-SVGCoordinateSystemViewBox);本文的矩阵运算只需要高中水平的二元一次方程。
学习目标:掌握五种变换函数的参数含义与坐标效果;理解”变换列表从右往左作用于坐标”这一核心规则;会用 rotate(angle cx cy) 与 transform-box/transform-origin 两种方式指定旋转中心;能识别绕原点旋转、描边被缩放两大经典陷阱。
前置知识:用户坐标系与 viewBox(《SVG 坐标系统与 viewBox》,003-SVGCoordinateSystemViewBox);本文的矩阵运算只需要高中水平的二元一次方程。
学习目标:掌握五种变换函数的参数含义与坐标效果;理解”变换列表从右往左作用于坐标”这一核心规则;会用 rotate(angle cx cy) 与 transform-box/transform-origin 两种方式指定旋转中心;能识别绕原点旋转、描边被缩放两大经典陷阱。
1. transform 属性
transform 对元素或分组应用几何变换,不影响后续元素的坐标系(除非在 <g> 上)。
<svg viewBox="0 0 200 200">
<rect x="50" y="50" width="50" height="50" fill="#4f5bd5" />
<rect x="50" y="50" width="50" height="50" fill="#d63031" transform="translate(60, 0)" />
</svg>
2. 基本变换函数
2.1 translate 平移
<rect transform="translate(50, 30)" />
<!-- 或单轴 -->
<rect transform="translate(50, 0)" />
| 参数 | 说明 |
|---|---|
tx | X 方向偏移 |
ty | Y 方向偏移(可省略,默认 0) |
2.2 rotate 旋转
<rect transform="rotate(45)" />
<!-- 围绕指定点旋转 -->
<rect transform="rotate(45 100 100)" />
| 参数 | 说明 |
|---|---|
angle | 旋转角度(度) |
cx, cy | 旋转中心(可省略,默认 0,0) |
单参数
rotate(45)围绕原点 (0,0) 旋转,通常不是想要的效果。常用rotate(angle cx cy)围绕元素中心旋转。方向提醒:SVG 的 y 轴向下,因此正角度是顺时针旋转(这与数学课本里 y 轴向上的坐标系正好相反,但与屏幕直觉一致)。
2.3 scale 缩放
<rect transform="scale(1.5)" />
<!-- 双轴独立 -->
<rect transform="scale(1.5, 0.5)" />
| 参数 | 说明 |
|---|---|
sx | X 方向缩放比 |
sy | Y 方向缩放比(可省略,默认等于 sx) |
scale 会同时缩放 stroke-width。若需保持描边不变,使用
vector-effect="non-scaling-stroke"。
2.4 skew 倾斜
<rect transform="skewX(30)" /> <rect transform="skewY(15)" />
| 函数 | 说明 |
|---|---|
skewX(angle) | 沿 X 轴倾斜 |
skewY(angle) | 沿 Y 轴倾斜 |
倾斜的本质是”y 坐标(或 x 坐标)越大,错切得越多”,常用于斜体文字、等轴测图形。
3. matrix 矩阵
所有变换本质都是 2D 仿射矩阵:matrix(a, b, c, d, e, f)。
| a c e |
| b d f |
| 0 0 1 |
变换公式:
x' = a*x + c*y + ey' = b*x + d*y + f
3.1 各变换对应的矩阵
| 变换 | matrix 参数 |
|---|---|
translate(tx, ty) | matrix(1 0 0 1 tx ty) |
rotate(θ) | matrix(cosθ sinθ -sinθ cosθ 0 0) |
scale(s) | matrix(s 0 0 s 0 0) |
skewX(θ) | matrix(1 0 tanθ 1 0 0) |
skewY(θ) | matrix(1 tanθ 0 1 0 0) |
3.2 示例
<rect transform="matrix(1 0 0 1 50 30)" />
<!-- 等价于 translate(50, 30) -->
<rect transform="matrix(0.707 0.707 -0.707 0.707 0 0)" />
<!-- 等价于 rotate(45) -->
4. 变换组合
多个变换用空格分隔,构成变换列表。矩阵层面它们按书写顺序依次左乘:transform="A B C" 等价于矩阵 A × B × C,作用到具体坐标点时是”从右往左”依次生效(最右边的变换最先接触元素自己的坐标),这一点与 CSS transform 一致。
一个不易失真的类比:变换列表像给元素层层套坐标系,每往右写一个函数,就往里再套一层,因此最右边的函数离元素最近、最先作用到它自身的坐标。
<!-- 先 translate 再 rotate(对点而言) -->
<rect transform="translate(100, 100) rotate(45)" />
<!-- 先 rotate 再 translate -->
<rect transform="rotate(45) translate(100, 0)" />
4.1 顺序影响结果
<svg viewBox="0 0 200 200">
<!-- 原始矩形 -->
<rect x="0" y="0" width="50" height="50" fill="#4f5bd5" />
<!-- 先平移到 (100,100) 再绕原点旋转 45°:矩形被甩到远处 -->
<rect
x="0"
y="0"
width="50"
height="50"
fill="#d63031"
transform="rotate(45) translate(100,100)"
/>
<!-- 先绕原点旋转 45° 再平移到 (100,100):矩形在 (100,100) 处旋转 -->
<rect
x="0"
y="0"
width="50"
height="50"
fill="#00b894"
transform="translate(100,100) rotate(45)"
/>
</svg>
建议:旋转用
rotate(angle cx cy)形式,避免顺序歧义。
5. transform-origin
SVG 2 引入 transform-origin,类似 CSS 的同名属性。
<rect
x="50"
y="50"
width="50"
height="50"
transform="rotate(45)"
transform-origin="75px 75px"
style="transform-box: fill-box"
/>
5.1 transform-box
| 值 | 含义 |
|---|---|
view-box | 以 SVG viewBox 为参考(默认) |
fill-box | 以元素边界框为参考 |
<style>
.spin {
transform-box: fill-box;
transform-origin: center;
animation: spin 2s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
<rect class="spin" x="50" y="50" width="100" height="100" fill="#4f5bd5" />
transform-box: fill-box+transform-origin: center是 SVG 元素围绕自身中心旋转的标准模式。
6. CSS transform 与 SVG transform
CSS transform 也可作用于 SVG 元素,但需注意坐标系差异。
6.1 CSS 方式
.logo {
transform: rotate(45deg);
transform-origin: 50% 50%;
transform-box: fill-box;
}
<svg viewBox="0 0 200 200">
<rect class="logo" x="50" y="50" width="100" height="100" fill="#4f5bd5" />
</svg>
6.2 两者区别
| 维度 | SVG transform 属性 | CSS transform |
|---|---|---|
| 语法 | rotate(45 100 100) | rotate(45deg) + transform-origin |
| 单位 | 无单位(默认度/像素) | 需 deg、px |
| 动画 | SMIL <animateTransform> | CSS @keyframes |
| 性能 | 略优(直接矩阵) | 现代浏览器已优化 |
| 浏览器支持 | 全部 | SVG 2 后完整支持 |
7. animateTransform 变换动画
SMIL 提供 <animateTransform> 专门用于变换动画。它的定义如今在独立的 SVG Animations 规范中(SVG 2 本体不强制要求实现),社区方向是 CSS 动画与 Web Animations API;不过所有主流浏览器至今仍完整支持,独立 .svg 文件(无法挂外部 CSS 的场景)里它仍是零依赖动画的首选,详见《SVG 动画基础》(013-SVGAnimationBasics)。
<svg viewBox="0 0 200 200">
<rect x="75" y="75" width="50" height="50" fill="#4f5bd5">
<animateTransform
attributeName="transform"
type="rotate"
from="0 100 100"
to="360 100 100"
dur="4s"
repeatCount="indefinite"
/>
</rect>
</svg>
7.1 多变换叠加 additive
<g>
<animateTransform
attributeName="transform"
type="translate"
values="0 0; 100 0; 0 0"
dur="4s"
repeatCount="indefinite"
/>
<animateTransform
attributeName="transform"
type="rotate"
values="0; 360"
dur="2s"
repeatCount="indefinite"
additive="sum"
/>
<rect x="-25" y="-25" width="50" height="50" fill="#4f5bd5" />
</g>
additive="sum" 让多个 animateTransform 叠加,否则后一个会覆盖前一个。
8. 嵌套变换
<g> 上的 transform 会作用于所有子元素,形成坐标系嵌套。
<svg viewBox="0 0 400 200">
<g transform="translate(100, 100)">
<!-- 子坐标系原点平移到 (100,100) -->
<g transform="rotate(45)">
<!-- 再旋转 45° -->
<rect x="-25" y="-25" width="50" height="50" fill="#4f5bd5" />
</g>
<circle cx="0" cy="0" r="5" fill="#d63031" />
</g>
</svg>
嵌套变换矩阵会相乘,最终变换是父子变换的复合。
9. 实战:地球绕太阳
<svg viewBox="0 0 400 400">
<!-- 太阳 -->
<circle cx="200" cy="200" r="40" fill="#f9a825" />
<!-- 地球轨道 -->
<circle cx="200" cy="200" r="120" fill="none" stroke="#ccc" stroke-dasharray="4 4" />
<!-- 地球绕太阳公转 -->
<g>
<animateTransform
attributeName="transform"
type="rotate"
from="0 200 200"
to="360 200 200"
dur="8s"
repeatCount="indefinite"
/>
<!-- 平移到轨道位置 -->
<g transform="translate(320, 200)">
<!-- 地球自转:给球面加一个"斑点"才能看出转动 -->
<g>
<animateTransform
attributeName="transform"
type="rotate"
from="0"
to="360"
dur="2s"
repeatCount="indefinite"
/>
<circle cx="0" cy="0" r="15" fill="#4f5bd5" />
<circle cx="6" cy="-6" r="3.5" fill="#2d4a9e" />
</g>
</g>
</g>
</svg>
结构:
- 外层
<g>绕太阳中心 (200,200) 旋转 → 实现公转 - 中层
<g>translate 到轨道位置 → 地球位置 - 内层
<g>绕自身原点 (0,0) 旋转 → 自转;注意纯色圆看不出旋转,必须给球面加一个不对称的标记(此处用一个小斑点)
这是”嵌套坐标系”最典型的应用:每层 <g> 只负责一件事,组合起来就是复杂运动。
10. 常见陷阱
10.1 rotate 默认绕原点
<!-- 错误:矩形被甩到画布外 -->
<rect x="50" y="50" width="100" height="100" transform="rotate(45)" />
<!-- 正确:围绕矩形中心旋转 -->
<rect x="50" y="50" width="100" height="100" transform="rotate(45 100 100)" />
10.2 scale 缩放描边
<!-- 描边被放大到 6px -->
<rect stroke-width="2" transform="scale(3)" />
<!-- 保持描边不变 -->
<rect stroke-width="2" transform="scale(3)" vector-effect="non-scaling-stroke" />
10.3 transform 与 viewBox 重复缩放
<!-- viewBox 已缩放 2 倍,transform scale(2) 会再缩 2 倍 -->
<svg viewBox="0 0 100 100" width="200" height="200">
<rect width="50" height="50" transform="scale(2)" />
<!-- 实际显示 200×200 -->
</svg>
小结
初学者要点:
rotate/scale默认以原点 (0,0) 为基准,而元素往往不在原点——要么用rotate(角度 cx cy)三参数形式,要么先把元素画在原点再translate到位。- 变换列表”从右往左”作用到元素坐标,顺序不同结果不同;拿不准时写一个测试用例对照。
- SVG 的 y 轴向下,正角度是顺时针;
scale会等比放大stroke-width,需要线宽恒定时用vector-effect="non-scaling-stroke"。
进阶注意:
transform-box: fill-box+transform-origin: center是 CSS 驱动 SVG 元素自转的标准组合;不设transform-box时默认参考 viewBox,origin: center会绕整个画布中心转,这是 CSS 动画 SVG 时最常见的”转错轴”原因。transform属性(SVG 2 起是 CSS transform 属性的表现属性形式)与 CSS 写法最终走同一条渲染管线,性能差异可忽略;选型依据是场景——独立.svg文件只能用属性/SMIL,内联 SVG 则两者皆可。- 复合运动(公转 + 自转)优先用嵌套
<g>分层表达,每层一个 transform,比手算复合矩阵可读得多。