SVG の g
要素は、子要素をグループ化して操作を行うための要素です。
ここでは、g
要素をどのような用途で利用できるのかをまとめておきます。
子要素に共通のスタイル(色、太さなど)をセットする
<svg width="195" height="60" style="border: solid 1px gray;">
<g fill="#cef" stroke="blue" stroke-width="1.5">
<rect x="15" y="15" width="30" height="30" />
<circle cx="75" cy="30" r="15" />
</g>
<g fill="#ffc" stroke="darkorange" stroke-width="1.5">
<rect x="105" y="15" width="30" height="30" />
<circle cx="165" cy="30" r="15" />
</g>
</svg>
この例では、2 つの g
要素の下に、rect
と circle
を 1 つずつ配置しています。
それらの塗りつぶし色 (fill
) や枠線の色 (stroke
) は、親の g
要素にセットしたものが使用されます。
多数の描画要素に同じスタイルを割り当てたいときは、g
要素でまとめてスタイル設定すると効率がよいです。
子要素の座標をまとめて移動する (transform)
<svg width="180" height="60" style="border: thin solid gray;">
<g transform="translate(50 30)">
<circle cx="0" cy="0" r="10" fill="red" />
<text x="15" y="2" dominant-baseline="middle"
font-size="20" font-weight="800" fill="blue">
Hello!
</text>
</g>
</svg>
g
要素の下に配置した子要素は、g
要素の transform
属性を使ってまとめて移動させることができます。
上記のサンプルコードでは、transform
属性の値として translate(50, 30)
を指定しており、g
要素でグルーピングされたすべての子要素が x 軸方向へ 50、y 軸方向へ 30 だけ平行移動しています。
g
要素をどこに配置するかは transform
属性で制御できるので、g
要素でグルーピングされた部分は、自分の座標系だけを意識したコンポーネントのように記述できます。
transform
の値には、次のような変換処理を指定することができます。
複数の変換処理をスペース区切りで並べて指定できます。
- 移動 …
translate(30 10)
で x 軸方向にへ 30、y 軸方向に 10 だけ平行移動します - 拡縮 …
scale(2 0.5)
で x 軸方向に 2 倍、y 軸方向に 0.5 倍にスケールします - 回転 …
rotate(30 10 10)
で (10, 10) の座標を中心に 30 度回転します - 歪み …
skewX(30)
、skewY(30)
で x 軸あるいは y 軸方向に 30 度歪ませます - 行列 …
matrix()
… 変換行列を適用して変換します (MDN)
例えば、次のように指定すると、平行移動、スキュー、スケールを順番に適用します。
<g transform="translate(35 30) skewX(-30) scale(1.5)">
ちなみに、このような g
要素を D3.js で生成する場合のコードは次のような感じになります。
同じ g
要素に circle
と text
を追加するため、g
要素を参照する D3 セレクションオブジェクトを変数化して使いまわすところがポイントです(下記の例では nodes
変数)。
あるいは、D3 セレクションオブジェクトの call
メソッドを使えば、次のようにメソッドチェーンですべて繋いで記述することも可能です。
どちらかというと、こちらの方が D3.js っぽい書き方なのかもしれません。
ちなみに、transform
属性は g
要素だけでなく、rect
などの個々の描画要素にも設定できます。
矩形を skewX
で変形させて菱形を作ったり、一時的に位置を移動させる場合などに使用できます。
<rect x="0" y="0" width="10" height="10"
transform="translate(30,40) rotate(45)" />
レイヤー構造を作り表示順序を制御する
D3.js を使った JavaScript コードなどで SVG を動的に構築する場合、各要素の表示順序(どちらが手前に表示されるか)が問題になったりします。
このようなケースでは、g
要素で表示順序を制御するだけのレイヤー構造を作り、そこに子要素を追加していくというテクニックが使えます。
次の例では、2 つの g
要素(backLayer
と frontLayer
)を作成し、その子要素として circle
や rect
要素を配置しています。
backLayer
、frontLayer
の順番で g
要素を追加しているので、frontLayer
に配置した子要素(この場合は circle
)の方が、手前に表示されることが保証されます。
<svg id="svg-rub6v9m" width="120" height="80" />
<script>
const svg = d3.select("#svg-rub6v9m")
const backLayer = svg.append("g");
const frontLayer = svg.append("g");
// frontLayer に追加したものは手前に表示される
frontLayer.append("circle")
.attr("cx", 75)
.attr("cy", 40)
.attr("r", 20)
.attr("fill", "red")
// backLayer に追加したものは奥に表示される
backLayer.append("rect")
.attr("x", 25)
.attr("y", 15)
.attr("width", 50)
.attr("height", 50)
.attr("fill", "blue");
</script>