SVGの変換順序:図形を拡大縮小して配置する
測定したロゴの例でSVGのtranslateとscaleの順序を比較します。入れ子グループを使って1点を追跡し、元のパスを変えずに図形を配置します。
SVG属性 transform="translate(60 25) scale(2)" では、元の点が親座標系へ移る際に2倍に拡大され、その後、横60単位、縦25単位移動します。リストを scale(2) translate(60 25) に逆転すると、その移動も2倍になります。図形のサイズは同じでも、位置は異なります。
サイズ変更でロゴが飛ぶ場合は、パスを編集する前に変換リストを確認します。配置用の外側のグループと、拡大縮小用の内側のグループは便利な構成です。2つの役割が明確なまま、後の編集用に元の図形座標も残せます。
順序を推測せず1点を追跡する
元の点 (10, 10) を考えます。倍率2、移動 (60, 25) では、2つのリストは次の結果になります。
| SVGのtransform属性 | 点の計算 | 親座標での最終点 |
|---|---|---|
translate(60 25) scale(2) | (10 × 2 + 60, 10 × 2 + 25) | (80, 45) |
scale(2) translate(60 25) | ((10 + 60) × 2, (10 + 25) × 2) | (140, 70) |
両方とも同じ元座標からの点です。2つ目は図形とともに移動も拡大します。最終位置は1つ目から (60, 25) 異なります。
順序の説明が矛盾して見える場合があります。SVG仕様は、リストを書いた順の入れ子座標系として説明します。左端の変換が外側のグループです。元の点をグループの外へ追跡するときは、最も内側の操作が先に作用します。W3Cのtransform属性の定義は、等価な入れ子を明示しています。
点の計算では右端から左へ進みます。グループ構造は外から内へ読みます。計算全体で解釈を統一してください。
同じ2部分の記号を比較する
次の完全な例を transform-order.svg として保存し、ブラウザーで開きます。図形はオリジナルの正方形の輪と別の三角形アクセントです。穴は偶奇塗り規則で定義した本当の空白です。
<svg xmlns="http://www.w3.org/2000/svg" width="700" height="400"
viewBox="0 0 350 200">
<g id="art" transform="translate(60 25) scale(2)">
<path fill="#147d78" fill-rule="evenodd"
d="M10 10H50V50H10Z M20 20H40V40H20Z"/>
<path fill="#e5a340" d="M65 40L85 10L105 40Z"/>
</g>
</svg>グループ属性だけを transform="scale(2) translate(60 25)" に変えます。記号はさらに右下へ移りますが、サイズと部分間の隙間は同じです。
私たちは両リストと入れ子グループの等価な構成をブラウザーで描画しました。比較パネルは同じ座標領域を使い、薄い記号が元の形状を示します。各赤点は元の点 (10, 10) の到達位置です。

元の図形のローカル枠は x=10、y=10、width=95、height=40 です。最初の構成のルート座標境界は x=80、y=45、width=190、height=80 になります。逆のリストは x=140、y=70 で、同じ 190 × 80 です。
これらは単純なストロークなしの例の測定です。アニメーションやCSS変換はありません。座標合成を示すもので、PerfectVectorの変換品質やすべてのSVG編集ソフトの互換性を示しません。
入れ子グループで配置とサイズを分ける
元のグループを次の構造に置き換え、パスは変えません。
<g transform="translate(60 25)">
<g id="art" transform="scale(2)">
<path fill="#147d78" fill-rule="evenodd"
d="M10 10H50V50H10Z M20 20H40V40H20Z"/>
<path fill="#e5a340" d="M65 40L85 10L105 40Z"/>
</g>
</g>1グループの translate(60 25) scale(2) と一致します。外側の移動が、拡大された座標系を配置します。内側の倍率を変えても、外側の移動値は (60, 25) のままです。
倍率を変えてもすべての見える点が固定されるという意味ではありません。ローカル原点から離れた点は、座標が掛け算されるため動きます。移動を選ぶ前に、どの元の点を図形の基準にするか決めてください。
たとえば輪の元の隅 (10, 10) を2倍の拡大後に (120, 80) に置きたいとします。計算は次のとおりです。
tx = targetX - scale × anchorX = 120 - 2 × 10 = 100
ty = targetY - scale × anchorY = 80 - 2 × 10 = 60translate(100 60) scale(2) を使えば、隅は (120, 80) に対応します。直接 (120, 80) だけ移動すると、ローカル原点がそこに置かれ、隅はさらに右下になります。
この計算は回転や傾斜のない一様な拡大縮小を想定します。意図的な回転中心にはSVGのtransform-originガイドを使います。無関係な補正を試行錯誤で合わせず、役割を持つグループに各変換を置いてください。
入れ子ファイルで描画時の対応を読む
読み込んだSVGには、複数の祖先に変換がある場合があります。最寄りのグループ属性だけでは合成効果を見落とします。MDNのtransformリファレンスは、同じ要素にCSSプロパティとプレゼンテーション属性があるとCSS変換が優先するとも説明します。属性変更が効かないようなら計算済みスタイルを確認します。
描画されたインラインSVGでは、次のコードで選んだグループの点をルートSVGのユーザー座標へ対応させます。
const root = document.querySelector('svg');
const art = document.querySelector('#art');
const rootMatrix = root.getScreenCTM();
const artMatrix = art.getScreenCTM();
if (!rootMatrix || !artMatrix) throw new Error('SVG is not measurable');
const toRoot = rootMatrix.inverse().multiply(artMatrix);
const point = new DOMPoint(10, 10).matrixTransform(toRoot);
const result = { x: point.x, y: point.y };ルート行列の逆行列が、共有する画面への対応を取り除きます。この例の 700 × 400 の表示は 350 × 200 のviewBoxの2倍なので、これが重要です。画面ピクセルとSVG座標は別単位です。MDNはgetScreenCTM()が返す対応を説明しています。
ブラウザー比較では、最初のリストと入れ子版は matrix(2 0 0 2 60 25)、逆のリストは matrix(2 0 0 2 120 50) でした。この拡大縮小と移動の例では、最後の2数値で移動の違いが明確になります。
コードは可逆な2次元変換を想定します。倍率0は逆行列の確認に適しません。重ね合わせ、収める書き出し、ストローク、回転した枠には、より詳しいSVGの境界の手順を使います。
図形グループの getBBox() が変換後のルート位置を返すとは考えないでください。getBBoxリファレンスのとおり、そのローカル結果は要素自身と祖先の変換を除きます。記号が見た目上動いてもローカル枠は同じ場合があります。
図形を作り直す前に変換を直す
SVGのコピーで作業します。一緒に動くパスを特定し、グループと祖先を確認します。分かりやすい基準点と、その目的地を記録します。
1つずつ変換を変えてください。サイズが正しく位置だけ違うなら、2順序の移動結果を比較します。部分が別々に動くなら、共通の配置を包含グループに置きます。回転中心と倍率順序の問題は分けます。
保存ファイルを利用先で開き直し、記号の全体範囲を確認します。正しい変換でもviewBoxやクリッピング領域の外へ動かすことはあります。SVG編集ガイドは、より広い整理と書き出しを扱います。
記号がPNGやJPGだけで編集可能な輪郭が必要なら、PerfectVectorのロゴベクター化ツールでSVG候補を作れます。シルエット、穴、分離した形状を確認し、復元図形をグループ化して編集ソフトやコードで配置変換を加えます。
ビットマップはベクター化しなくても移動や拡大縮小できます。編集可能な形状が必要な場合にトレースし、既存のベクターマスターがあれば保持します。正確なロゴ形状や文字には手動再構築が必要な場合もあり、ロゴベクター化ガイドは開始方法の選択を説明します。
よくある質問
SVGは変換を左から右、右から左のどちらで適用しますか? リストは書いた順に外から内への入れ子座標系に対応します。元の点の最終位置を計算する場合は、右端の関数を先に適用し、左へ向かって外へ進みます。
translateとscaleを逆にするとロゴが移動するのはなぜですか? 属性リストでscaleの後にtranslateがあると、scaleは移動も掛け算します。translateの後にscaleがあると、移動はその倍率の外側に残ります。そのため同じ値でもサイズが同じで位置が異なる結果になります。
外側の移動で、内側をサイズ変更しても全点が固定されますか? いいえ。外側の移動値を内側の倍率から分けるだけです。ローカル原点から離れた点は拡大縮小で動きます。基準点を選び、その配置に必要な移動を計算してください。
参考資料
- W3C — SVGのtransform属性 — 変換リストと入れ子座標系の関係を定義しています。
- MDN — SVGのtransform — 構文、行列の対応、CSSの優先を説明しています。
- MDN — getScreenCTM — SVG座標を文書のビューポートへ対応させる行列を説明しています。
- MDN — getBBox — ローカル境界と、測定要素の変換の除外を説明しています。
元のロゴに編集可能な形状が必要なら、PerfectVectorでSVG候補を作成し、輪郭と別々の部分を確認してください。その後、基準点を選び、拡大縮小後の最終位置を検証します。

