座標変換(transform)
translate / rotate / scale / skew / matrix で要素を変形。 SVG 属性版と CSS transform 版の違い、適用順序、回転中心まで。
SVG transform 属性
<rect width="60" height="60" transform="translate(20, 30)" />
<g transform="translate(50,50) rotate(45)">
<rect x="-30" y="-30" width="60" height="60" />
</g>
主要な変換関数
| 関数 | 意味 |
|---|---|
| translate(dx, dy) | 平行移動 |
| rotate(angle) | 原点中心に回転(度) |
| rotate(angle, cx, cy) | (cx, cy) を中心に回転 |
| scale(sx, sy) | 拡大縮小(sy 省略で sx と同じ) |
| skewX(angle) / skewY(angle) | 斜め変形 |
| matrix(a b c d e f) | 行列を直接 |
適用順序(重要)
スペース区切りで複数指定すると左から右の順で適用:
<g transform="translate(100,100) rotate(45) scale(2)">
↑
scale → rotate → translate の順で行列が掛かる
(結果: 中心 (100,100) を原点とした座標系で 45 度回転 + 2 倍)
</g>
「中心を基準に回転」の典型
<!-- 方法 1: rotate に中心を指定 -->
<rect x="40" y="40" width="20" height="20"
transform="rotate(45 50 50)" />
<!-- 方法 2: translate して回転 -->
<g transform="translate(50,50) rotate(45)">
<rect x="-10" y="-10" width="20" height="20" />
</g>
scale でちょっと注意
- 原点(0,0)を中心に拡大される
- 「中心を保って拡大」は
translate+scale+translate(逆) - scale は線の太さも拡大する → vector-effect="non-scaling-stroke"
matrix の中身
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
他の関数は matrix の特殊形。
CSS transform との違い
| SVG 属性 | CSS transform | |
|---|---|---|
| 記述 | transform="..." | style="transform: ..." |
| 原点 | SVG 座標系の (0,0) | 要素の中心 50% 50%(既定) |
| 単位 | 数値のみ | px / deg / % など |
| 3D | 不可 | translate3d / perspective |
| アニメ | SMIL or CSS | CSS が GPU 加速 |
CSS で SVG を transform
svg .icon {
transform: rotate(45deg);
transform-origin: center;
transition: transform 0.3s;
}
svg .icon:hover { transform: rotate(90deg) scale(1.2); }
transform-origin の指定
- CSS:
transform-origin: 50% 50%またはcenter - SVG 属性のみ: rotate(angle, cx, cy) などで明示
g でグルーピングする
複数の要素をまとめて変形するなら <g>:
<g transform="translate(100, 100)">
<circle r="20" />
<rect x="-30" y="-30" width="60" height="60" fill="none" stroke="black"/>
</g>
変換の入れ子
親の transform は子に累積される:
<g transform="translate(50, 50)">
<g transform="rotate(45)">
<rect x="-10" y="-10" width="20" height="20" />
<!-- 結果: (50,50) に置かれて 45 度回転 -->
</g>
</g>
scaleX / scaleY 個別
<rect transform="scale(2, 1)" /> <!-- 横だけ 2 倍 -->
<rect transform="scale(-1, 1)" /> <!-- 反転 -->
skewX / skewY
<rect transform="skewX(20)" />
<rect transform="skewY(-15)" />
斜め文字や絵作りに使えるが、レイアウトを大きく崩すので慎重に。
反転 / ミラー
<g transform="translate(100, 0) scale(-1, 1)">
<text x="0" y="20">Hello</text>
</g>
JS から transform を変える
el.setAttribute("transform", `rotate(${angle} ${cx} ${cy})`)
// あるいは CSS
el.style.transform = `rotate(${angle}deg)`
動的な回転(時計の針)
function tick() {
const now = new Date()
const sec = now.getSeconds() + now.getMilliseconds() / 1000
hand.setAttribute("transform", `rotate(${sec * 6} 100 100)`)
requestAnimationFrame(tick)
}
tick()
SVG 2 の CSS 互換
SVG 2(実装が進行中)では CSS で transform: translate(20px, 30px) のように単位付きで書ける。
現代のブラウザはほぼ対応。
複雑な変換のレシピ
左下を原点に
<g transform="translate(0, height) scale(1, -1)">
<!-- y が上向きになる(数学座標系) -->
</g>
360 度時計回りアニメ
<g style="transform-origin: center; animation: spin 2s linear infinite">
<path d="..." />
</g>
<style>
@keyframes spin { to { transform: rotate(360deg); } }
</style>
パスに沿って動かす
.dot {
offset-path: path("M 10 50 Q 50 0 90 50 T 170 50");
offset-distance: 0%;
animation: move 3s linear infinite;
}
@keyframes move { to { offset-distance: 100%; } }
失敗パターン
| 症状 | 対処 |
|---|---|
| 回転中心がずれる | rotate(angle, cx, cy) or transform-origin |
| scale で線も太くなる | vector-effect="non-scaling-stroke" |
| 要素が消えた | 巨大な translate / scale で画面外 |
| アニメが GPU で遅い | SVG 属性ではなく CSS transform を使う |
| 順序が想定と違う | 変換は左から、入れ子は親→子の順 |
迷ったら
静的な配置はSVG 属性、アニメはCSS transformが定石。 GPU 加速されるので滑らかに動く。