变换
SVG 变换允许你移动、旋转、缩放和倾斜图形元素。变换可以应用于单个元素或一组元素,是创建复杂图形和动画的基础。
变换基础
使用 transform 属性对元素应用变换。变换会创建一个新的坐标系统,元素在这个新坐标系统中渲染。
<svg width="200" height="100" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="50" height="50" fill="#bdc3c7"/>
<rect x="10" y="10" width="50" height="50" fill="#3498db" transform="translate(100, 20)"/>
</svg>
平移 - translate
translate(tx, ty) 将元素沿 x 和 y 轴移动指定距离。
<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="50" height="50" fill="#e74c3c"/>
<rect x="10" y="10" width="50" height="50" fill="#3498db" transform="translate(80, 0)"/>
<rect x="10" y="10" width="50" height="50" fill="#2ecc71" transform="translate(160, 20)"/>
</svg>
如果省略 ty,则默认为 0。
理解变换的本质:变换不是移动元素本身,而是移动元素的坐标系统。元素仍然在"原来的位置"绘制,只是坐标系统变了。
旋转 - rotate
rotate(angle, cx, cy) 将元素围绕指定点旋转指定角度。角度以度为单位,正值表示顺时针旋转。
围绕原点旋转
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="50" y="50" width="50" height="50" fill="#e74c3c" opacity="0.3"/>
<rect x="50" y="50" width="50" height="50" fill="#e74c3c" transform="rotate(30)"/>
</svg>
当只指定角度时,元素围绕 SVG 原点 (0, 0) 旋转。
围绕指定点旋转
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="50" y="50" width="50" height="50" fill="#3498db" opacity="0.3"/>
<rect x="50" y="50" width="50" height="50" fill="#3498db" transform="rotate(45, 75, 75)"/>
<circle cx="75" cy="75" r="3" fill="#e74c3c"/>
</svg>
rotate(45, 75, 75) 表示围绕点 (75, 75) 旋转 45 度。这个点通常是元素的中心。
旋转示例
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(75, 75)">
<rect x="-25" y="-25" width="50" height="50" fill="#9b59b6" transform="rotate(0)"/>
</g>
<g transform="translate(150, 75)">
<rect x="-25" y="-25" width="50" height="50" fill="#9b59b6" transform="rotate(15)"/>
</g>
<g transform="translate(225, 75)">
<rect x="-25" y="-25" width="50" height="50" fill="#9b59b6" transform="rotate(30)"/>
</g>
</svg>
通过将元素中心放在原点,旋转后再平移到目标位置,可以简化围绕中心旋转的操作。
缩放 - scale
scale(sx, sy) 将元素沿 x 和 y 轴缩放指定比例。
统一缩放
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="20" width="50" height="50" fill="#e74c3c"/>
<rect x="20" y="20" width="50" height="50" fill="#3498db" transform="scale(1.5)"/>
<rect x="20" y="20" width="50" height="50" fill="#2ecc71" transform="scale(2)"/>
</svg>
如果省略 sy,则 sy 等于 sx,实现等比例缩放。
非均匀缩放
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="75" r="30" fill="#e74c3c"/>
<ellipse cx="150" cy="75" rx="45" ry="30" fill="#3498db"/>
<ellipse cx="250" cy="75" rx="30" ry="60" fill="#2ecc71"/>
</svg>
缩放的原点问题
缩放以原点为中心进行,这可能导致元素位置偏移:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="50" y="50" width="50" height="50" fill="#e74c3c" opacity="0.3"/>
<rect x="50" y="50" width="50" height="50" fill="#e74c3c" transform="scale(1.5)"/>
</svg>
要围绕元素中心缩放,需要先平移到原点,缩放后再平移回来:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="50" y="50" width="50" height="50" fill="#3498db" opacity="0.3"/>
<rect x="50" y="50" width="50" height="50" fill="#3498db"
transform="translate(75, 75) scale(1.5) translate(-75, -75)"/>
</svg>
负值缩放
负值缩放会产生镜像效果:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<text x="20" y="60" font-size="24" fill="#e74c3c">正常文本</text>
<text x="280" y="60" font-size="24" fill="#3498db" transform="scale(-1, 1)">镜像文本</text>
</svg>
斜切 - skewX 和 skewY
skewX(angle) 沿 x 轴斜切,skewY(angle) 沿 y 轴斜切。
skewX
<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="20" width="50" height="50" fill="#e74c3c"/>
<rect x="100" y="20" width="50" height="50" fill="#3498db" transform="skewX(20)"/>
<rect x="180" y="20" width="50" height="50" fill="#2ecc71" transform="skewX(40)"/>
</svg>
skewY
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="20" width="50" height="50" fill="#e74c3c"/>
<rect x="100" y="20" width="50" height="50" fill="#3498db" transform="skewY(20)"/>
<rect x="180" y="20" width="50" height="50" fill="#2ecc71" transform="skewY(40)"/>
</svg>
组合斜切
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="100" y="50" width="60" height="60" fill="#9b59b6"
transform="skewX(15) skewY(15)"/>
</svg>
矩阵变换 - matrix
所有变换都可以用一个 3x3 变换矩阵表示。matrix(a, b, c, d, e, f) 直接指定变换矩阵:
| a c e |
| b d f |
| 0 0 1 |
变换公式:
x' = a*x + c*y + e
y' = b*x + d*y + f
基本变换对应的矩阵
| 变换 | 矩阵参数 |
|---|---|
| translate(tx, ty) | matrix(1, 0, 0, 1, tx, ty) |
| scale(sx, sy) | matrix(sx, 0, 0, sy, 0, 0) |
| rotate(a) | matrix(cos(a), sin(a), -sin(a), cos(a), 0, 0) |
| skewX(a) | matrix(1, 0, tan(a), 1, 0, 0) |
| skewY(a) | matrix(1, tan(a), 0, 1, 0, 0) |
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="20" width="50" height="50" fill="#e74c3c"/>
<rect x="20" y="20" width="50" height="50" fill="#3498db"
transform="matrix(1, 0, 0, 1, 100, 30)"/>
</svg>
变换组合
多个变换可以组合使用,按从右到左的顺序执行:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="50" y="50" width="50" height="50" fill="#e74c3c" opacity="0.3"/>
<rect x="50" y="50" width="50" height="50" fill="#3498db"
transform="translate(100, 0) rotate(30)"/>
</svg>
这个例子中,先执行 rotate(30),再执行 translate(100, 0)。
变换顺序的影响
变换顺序不同会产生不同结果:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="50" y="50" width="50" height="50" fill="#e74c3c" opacity="0.3"/>
<rect x="50" y="50" width="50" height="50" fill="#3498db"
transform="translate(100, 0) rotate(30)"/>
<rect x="50" y="50" width="50" height="50" fill="#2ecc71"
transform="rotate(30) translate(100, 0)"/>
</svg>
蓝色矩形先旋转后平移,绿色矩形先平移后旋转,结果完全不同。
围绕中心旋转的正确方法
要围绕元素中心旋转,正确的变换顺序是:平移到原点 → 旋转 → 平移回原位置:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="100" y="50" width="60" height="60" fill="#9b59b6" opacity="0.3"/>
<rect x="100" y="50" width="60" height="60" fill="#9b59b6"
transform="translate(130, 80) rotate(45) translate(-130, -80)"/>
</svg>
transform-origin 属性
CSS 的 transform-origin 属性可以简化围绕特定点变换的操作:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<style>
.rotate-center {
transform-origin: center;
transform: rotate(30deg);
}
</style>
<rect x="100" y="50" width="60" height="60" fill="#1abc9c" class="rotate-center"/>
</svg>
注意:使用 CSS transform 时,角度单位需要用 deg。
嵌套坐标系统
<svg> 元素可以嵌套,内层 SVG 创建新的坐标系统:
<svg width="300" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="80" height="60" fill="#e74c3c"/>
<svg x="100" y="10" width="100" height="100" viewBox="0 0 50 50">
<rect x="5" y="5" width="40" height="40" fill="#3498db"/>
</svg>
</svg>
内层 SVG 的 viewBox="0 0 50 50" 定义了 50x50 的坐标系统,但显示区域是 100x100,所以内容被放大了两倍。
viewBox 与变换
viewBox 属性本质上是一种变换,它将用户坐标映射到视口坐标:
<svg width="200" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="80" height="80" fill="#f39c12"/>
</svg>
viewBox="0 0 100 100" 定义了 100x100 的用户坐标系统,显示在 200x100 的视口中,x 方向被放大 2 倍,y 方向保持不变。
实用示例
旋转的风车
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(100, 100)">
<polygon points="0,-80 20,-20 0,0 -20,-20" fill="#e74c3c" transform="rotate(0)"/>
<polygon points="0,-80 20,-20 0,0 -20,-20" fill="#3498db" transform="rotate(90)"/>
<polygon points="0,-80 20,-20 0,0 -20,-20" fill="#2ecc71" transform="rotate(180)"/>
<polygon points="0,-80 20,-20 0,0 -20,-20" fill="#f39c12" transform="rotate(270)"/>
<circle r="15" fill="#2c3e50"/>
</g>
</svg>
3D 立方体
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(100, 100)">
<polygon points="-40,-20 40,-20 40,60 -40,60" fill="#3498db"/>
<polygon points="-40,-20 0,-50 80,-50 40,-20" fill="#2980b9"/>
<polygon points="40,-20 80,-50 80,30 40,60" fill="#1a5276"/>
</g>
</svg>
阴影效果
<svg width="200" height="100" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="100" cy="80" rx="60" ry="10" fill="#bdc3c7"/>
<rect x="60" y="20" width="80" height="60" rx="5" fill="#3498db"
transform="skewX(-10)"/>
</svg>
小结
变换是 SVG 中操作图形位置、大小和方向的核心工具。平移移动元素,旋转改变方向,缩放改变大小,斜切产生倾斜效果。变换可以组合使用,但顺序很重要。理解变换的本质是操作坐标系统,有助于正确应用变换实现预期效果。