スコット・マレイ
コード・アーティスト

Tutorials > D3 > SVG の基本

SVG の基本

最終更新日 2012年12月30日(原文)  2013年04月02日(翻訳 / h.sakai

D3 が一番力を発揮するのは、ビジュアルを SVG で生成し、操作する場合です。前章で学んだように div 等のネイティブな HTML 要素を使って描画する方法もあります。しかしこれはあまりスマートな方法と言えない上、ブラウザ間の非互換性のために表示の一貫性も欠いてしまいがちです。SVG を使うことで、より信頼性が高く、一貫性があり、高速な描画が可能になるのです。

SVG ファイルはイラストレータのようなベクターイメージ編集ソフトで生成できますが、D3 で必要となるのは、コーディングによって動的に SVG を生成する方法です。

SVG 要素

SVG(スケーラブル・ベクター・グラフィックス)はテキストベースの画像フォーマットです。SVG 画像は HTML に似たマークアップ言語で記述します。SVG のコードは、直接 HTML 文書内に記述することが可能です。インターネット・エクスプローラのバージョン8以前除くすべてのブラウザは SVG をサポートしています。SVG は XML をベースとしているため、終了タグを持たない要素は自己終了タグでなければなりません。例を挙げてみます。

<element></element>   <!-- 終了タグがある -->
<element/>            <!-- 自己終了タグ -->

何を描くにしろ、最初にすべきことは SVG 要素の生成です。SVG 要素は、これから絵を描いて行くためのキャンバスだと理解してください(この観点から、 SVG は HTML の canvas 要素に似た概念であると言えます)。SVG 要素を生成する際には、最低限 widthheight の値を設定しておく方がよいでしょう。さもないと、SVG は親要素のすべての領域を占有してしまうからです。

<svg width="500" height="50">
</svg>

このコードで生成した SVG が次の画像です。

なにも見えませんね?上の空白スペース上でマウスを右クリックし、コンテキストメニューから「要素を検証」を選んでみましょう。WEB インスペクタが開き、下のような画面が表示されるはずです( Google Chrome ブラウザの場合)。WEB インスペクタの反転表示されている行にマウスカーソルを持っていけばブラウザ上の対応する領域がハイライト表示されます。

Inspecting an empty SVG

svg 要素が表示され、横 500 ピクセル、縦 50 ピクセルを占めてますね?残念ながら、中身はまだ空っぽです。

ブラウザがデフォルトの単位として「ピクセル」を用いたことに注意してください。コード上では、500px や 50px ではなく、500 や 50 としか指定していません。明示的に px を指定することも、あるいは emptincmmm等、サポートされている任意の単位を指定することもできます。

簡単なシェイプ

svg タグ中に記述できるビジュアル要素は多数あります。 rectcircleellipselinetextline などです。

svg はピクセルベースの座標系で、描画領域の左上が原点(0, 0)となります。コンピュータグラフィックスのプログラマにはお馴染みの座標系でしょう。x の値が増加するにつれ右に移動し、y の値が増加するにつれ下に移動します。

0,0 100,20 200,40

rect は長方形を描きます。xy で長方形の左上の座標を指定し、 widthheight でサイズを指定します。次の例では、長方形は親要素 SVG の全領域を占めています。

<rect x="0" y="0" width="500" height="50"/>

circle は円を描きます。cxcy で円の中心の座標を指定し、r で半径( radius )を指定します。次の例では、cx( cx は "center-x" の略)に 250を 指定しているため、円は 500 ピクセル幅の SVG 領域の中心に位置します。

<circle cx="250" cy="25" r="25"/>

ellipse は楕円を描きます。circle に似ていますが、円と違うのは横方向(rx)と縦方向(ry)のそれぞれの半径を指定する必要があることです。

<ellipse cx="250" cy="25" rx="100" ry="25"/>

line は線を描きます。x1y1 で線分の一方の端の座標を指定し、x2y2 でもう一方の端の座標を指定します。線を表示するためには stroke で線の色を指定しなければなりません。

<line x1="0" y1="0" x2="500" y2="50" stroke="black"/>

text は文字を描きます。x で文字の左端の座標を指定し、y で文字の ベースラインの縦座標を指定します(アセンダライン=上端の座標でなく、ベースラインの座標であることに注意してください)。他のシェイプ要素とは異なり、text 要素は開始タグと終了タグで表示する文字列を囲みます。

<text x="250" y="25">javascript</text>

javascript

特に指定のない限り、text は CSS で指定された親要素のフォントスタイルを継承します。上のサンプルの文字のフォーマットが、地の文のフォーマットと同じである点に注意してください。このフォーマットを上書きするには以下のように指定します。文字色の指定には fill を使います。

<text x="250" y="25" font-family="sans-serif"
   font-size="25" fill="gray">javascript</text>

javascript

ビジュアル要素についてもう一つ注意しておくべき点は、SVG の境界を超える部分が切れてしまうということです。特に text を使う場合、ディセンダ部分(文字のベースラインより下の部分)が切れないよう注意する必要があります。これは、下のサンプルのように、ベースラインの座標(y)が SVG の高さ(50)と同じ場合などに発生します。

<text x="250" y="50" font-family="sans-serif"
   font-size="25" fill="gray">javascript</text>

javascript

ベースライン、アセンダライン、ディセンダラインの関係は下図のようになります。赤丸の位置が text で指定する際の xy の座標になります。

baseline, ascender line, descender line

path は、ここに述べた単純なシェイプ以外の、もっと複雑な形状を描く場合に用います(地図で国境線を描く場合など)。path についてはいつか改めて説明したいと思います。この章では単純なシェイプのみを扱います。

SVG 要素のスタイル設定

デフォルトの SVG のスタイルは、fill が黒、stroke は無しとなっています。それ以外の設定をしたい場合は自分でスタイルを指定する必要があります。以下、一般的な SVG のプロパティについて解説します。

text には次のプロパティも設定可能です。指定方法は CSS と同様です。

普通の HTML 要素と SVG 要素のもう一つの類似点は、いずれも二通りの方法でスタイルを設定できることです。一つは直接(インラインで)要素の属性として設定する方法、もう一つは CSS スタイルルールを適用する方法です。

次の例では、circle 要素に属性としていくつかのプロパティを直接設定しています。

<circle cx="25" cy="25" r="22"
   fill="yellow" stroke="orange" stroke-width="5"/>

CSS で行うには、まず circle からスタイル属性を取り除いてクラスを割り当てます(通常の HTML 要素と同様の方法です)。

<circle cx="25" cy="25" r="22" class="pumpkin"/>

次に CSS で、そのクラスに fillstrokestroke-width の各ルールを設定します。

.pumpkin {
   fill: yellow;
   stroke: orange;
   stroke-width: 5;
}

両者の比較では、CSS の方にいくつか明瞭なメリットがあります。

  1. 一度設定したスタイルを、複数の要素に適用できる。
  2. 一般に、インライン属性より CSS コードの方が読みやすい。
  3. 以上の理由で、メンテナンス性が高く、デザイン変更が容易。

CSS で SVG要素のスタイル設定をしていると、ほかの一般的な HTML 要素の設定と混乱してしまうことがあります。fillstrokestroke-width が CSS のプロパティではないからです(よく似た意味の CSS プロパティに background-colorborderがあります)。この場合、セレクタに要素名 svg を入れておけば、どのルールが SVG 用途なのかが分かり易くなるでしょう。

svg .pumpkin {
   /* ... */
}

レイヤーと描画の順序

SVG にはレイヤも深度の概念もありません。また、CSS の z-index プロパティもサポートしていません。したがってシェイプはすべて単一の x/y 二次元平面上に配置されます。

しかし、複数のシェイプを描くと、下図のように重ねて配置されます。

<rect x="0" y="0" width="30" height="30" fill="purple"/>
   <rect x="20" y="5" width="30" height="30" fill="blue"/>
   <rect x="40" y="10" width="30" height="30" fill="green"/>
   <rect x="60" y="15" width="30" height="30" fill="yellow"/>
   <rect x="80" y="20" width="30" height="30" fill="red"/>

各シェイプの深さの順(重ねられる順)が、要素のコードが書かれた順によって決まるからです。紫の四角形のコードは一番最初に書かれており、したがって最初に描画されます。次に青の四角形が青の「上」に描かれ、その上に緑、と続きます。

SVG のシェイプは、キャンバスに描かれる絵と同じように描画されると理解してください。後からピクセル描画されたシェイプは、先に描かれたシェイプを覆い隠し、その結果「手前」に表示されるのです。

あるビジュアル要素を、常にほかの要素の前面に表示したい場合、この描画順序の仕組みが重要になります。たとえば散布図に軸や値ラベルなどを描く場合です。軸とラベルを一番最後に SVG に追加することで、他の要素の前面に表示することができます。

透過性

視覚化作業において、要素に透過性を与えることは時に大変効果を発揮します。たとえば重なった要素を見えるようにしておきたい場合や、 ある要素を強調し、他の要素を弱めたい場合などです。

透過度の設定には二つの方法があります。アルファ付の RGB 色を使う方法と、opacity に値を設定する方法です。

fillstroke など、色を指定する場所では常に rgba() を使うことができます。rgba() には、赤、緑、青それぞれに 0 から 255 の間の値、アルファ値(透明度)に 0.0 から 1.0 の間の値を指定します。

<circle cx="25" cy="25" r="20" fill="rgba(128, 0, 128, 1.0)"/>
<circle cx="50" cy="25" r="20" fill="rgba(0, 0, 255, 0.75)"/>
<circle cx="75" cy="25" r="20" fill="rgba(0, 255, 0, 0.5)"/>
<circle cx="100" cy="25" r="20" fill="rgba(255, 255, 0, 0.25)"/>
<circle cx="125" cy="25" r="20" fill="rgba(255, 0, 0, 0.1)"/>

rgba()fillstroke に個別に透明度を設定できる点に注意してください。次の図の円は、fill の透明度が 75%、stroke の透明度が 25% になっています。なお、円が三重に見えているのは、stroke-width の太さは円周の外側に 10 ピクセルではなく、円周を中心に外側に 5 ピクセル、内側に 5 ピクセルずつ広がっていて(合計 10 ピクセル )、内側が fill の色と重なっているためです。

<circle cx="25" cy="25" r="20"
        fill="rgba(128, 0, 128, 0.75)"
        stroke="rgba(0, 255, 0, 0.25)" stroke-width="10"/>
<circle cx="75" cy="25" r="20"
        fill="rgba(0, 255, 0, 0.75)"
        stroke="rgba(0, 0, 255, 0.25)" stroke-width="10"/>
<circle cx="125" cy="25" r="20"
        fill="rgba(255, 255, 0, 0.75)"
        stroke="rgba(255, 0, 0, 0.25)" stroke-width="10"/>

要素全体に透明度を適用したい場合は opacity 属性を設定します。次の図はいずれも透明度 1.0 (完全に不透明)の状態の円です。

同じ円に、順に opacity 0.9、0.5、0.1 を設定します(値が 0.0 に近づくほど透明)。

<circle cx="25" cy="25" r="20" fill="purple"
        stroke="green" stroke-width="10"
        opacity="0.9"/>
<circle cx="65" cy="25" r="20" fill="green"
        stroke="blue" stroke-width="10"
        opacity="0.5"/>
<circle cx="105" cy="25" r="20" fill="yellow"
        stroke="red" stroke-width="10"
        opacity="0.1"/>

rgba() 色を設定した要素に、さらに opacity を設定することもできます。その場合の透明度は、両者の透明度の積となります。次の三つの円の fillstroke の RGBA 値の組み合わせは同一ですが、最初の円には opacity は設定されておらず、残りの二つの円には、それぞれ 0.5 と 0.2 の opacity が設定されています。

<circle cx="25" cy="25" r="20"
        fill="rgba(128, 0, 128, 0.75)"
        stroke="rgba(0, 255, 0, 0.25)" stroke-width="10"/>
<circle cx="65" cy="25" r="20"
        fill="rgba(128, 0, 128, 0.75)"
        stroke="rgba(0, 255, 0, 0.25)" stroke-width="10"
        opacity="0.5"/>
<circle cx="105" cy="25" r="20"
        fill="rgba(128, 0, 128, 0.75)"
        stroke="rgba(0, 255, 0, 0.25)" stroke-width="10"
        opacity="0.2"/>

三つ目の円では、円の opacity0.2、つまり 20% です。しかし紫の fill にはすでにアルファ値として 0.75、つまり 75% が設定されています。その結果、最終的な透明度は、0.2 と 0.75 の積で 0.15、すなわち 15% となるのです。

次章はSVG の描画

インタラクティブ・データ・ヴィジュアライゼーション このチュートリアルの書籍版の翻訳がオライリー・ジャパンより発売されました。タイトルは『インタラクティブ・データビジュアライゼーション ―D3.jsによるデータの可視化』です(画像をクリックするとアマゾンに飛びます)。

このチュートリアルを大幅に拡充し、3倍近い内容となっています。JavaScriptを中心に基礎編をさらに詳しく解説し(書籍版第3章)、応用編としてモーション、イベント、レイアウト、地図の作成法、データのエクスポート(PDFやSVG等)の章が追加されています(同9章~13章)。アマゾンのページで目次を見ることができます。

本チュートリアルがわかりにくいと感じられた方、あるいは本チュートリアルを終え、さらに応用力を身につけたいと思われた方のどちらにもお勧めの内容となっています。

翻訳はコンピュータ・プログラミング関連書籍を多数翻訳されている長尾高弘氏です。