座標変換(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 でちょっと注意

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 CSSCSS が 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 の指定

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 加速されるので滑らかに動く。