【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

SVGを使ってみよう

SVG はちょっとした図なら手書きできます。取っ掛かりとして基本的な事項を説明します。

※ 記事執筆者自身による改訂版です。はてなブログの仕様に合わせて修正しています。

【元記事】SVGを使ってみよう - MathWills

目次

サイズ

SVG は Scalable Vector Graphics の略です。PNGJPEG などの画像が点(ピクセル)の集まりで構成されているのに対して、SVG は線や円などの図形情報の集まりで構成されています。そのため SVG は拡大・縮小しても画質が保たれるという特徴があります。

固定

初めにサイズを固定する方法から説明します。svg タグにサイズを指定して、その中に円を置きます。

<svg width="100" height="50">
    <circle cx="25" cy="25" r="22"></circle>
    <circle cx="75" cy="25" r="22" fill="red"></circle>
</svg>

※ この SVG を編集画面に貼り付ければ、図が表示されます。

座標は左上を原点とします。circle の cx, cy は中心の座標、r は半径です。デフォルトでは黒く塗りつぶされますが、fill によって色が変えられます。

※ 数学のグラフで使われる座標とは縦方向の向きが逆です。向きを合わせる方法は後述します。

閉じタグ

circle には従属要素がないため、直後にタグを閉じています。これは以下のように略記できます。

<svg width="100" height="50">
    <circle cx="25" cy="25" r="22" />
    <circle cx="75" cy="25" r="22" fill="red" />
</svg>

タグの最後のスラッシュ '/' によって、タグがそこで閉じられていることを示します。以後、こちらの書式を使用します。

SVG のタグは必ず閉じる必要があるため、スラッシュを省略することはできません。例えば 1 番目の circle のスラッシュを省略すれば 2 番目の circle が描画されなくなります。

<!-- ダメな例 -->
<svg width="100" height="50">
    <circle cx="25" cy="25" r="22">
    <circle cx="75" cy="25" r="22" fill="red" />
</svg>

書いたはずの図形が表示されない場合は、まずタグが閉じられているかを確認してください。

グループ

円の外形だけを描画するには、stroke で線の色を指定して、fill="none" とします。

<svg width="100" height="50">
    <circle cx="25" cy="25" r="22" stroke="black" fill="none" />
    <circle cx="75" cy="25" r="22" stroke="black" fill="none" />
</svg>

毎回同じ stroke や fill を記述するのは手間です。g タグで指定して囲むことで、括り出すことができます。

<svg width="100" height="50">
    <g stroke="black" fill="none">
        <circle cx="25" cy="25" r="22" />
        <circle cx="75" cy="25" r="22" />
    </g>
</svg>

拡大

図のサイズと座標とは別々に指定することができます。

先ほどの図を拡大してみます。

<svg width="200" height="100" viewbox="0 0 100 50">
    <circle cx="25" cy="25" r="22" />
    <circle cx="75" cy="25" r="22" fill="red" />
</svg>

viewbox="0 0 100 50" は図で使用される座標系を指定します。この場合、画像のサイズは 200×100 ですが、その中に割り振られる座標は $0≦x<100,0≦y<50$ となります。つまり 2 倍に拡大されます。

線には太さの概念があり、拡大の対象となります。stroke-width で指定します。デフォルトは 1 です。

<svg width="200" height="100" viewbox="0 0 100 50">
    <g stroke-width="0.5" stroke="black" fill="none">
        <circle cx="25" cy="25" r="22" />
        <circle cx="75" cy="25" r="22" />
    </g>
</svg>

画像のサイズ(width, height)を省略して viewbox だけを指定すれば、横幅一杯に拡大されます。大きめの図にはこれを使用するのが良いでしょう。スマホや PC など画面サイズが異なっても、自動的に調整されます。

<svg viewbox="0 0 100 50">
    <g stroke-width="0.5" stroke="black" fill="none">
        <circle cx="25" cy="25" r="22" />
        <circle cx="75" cy="25" r="22" />
    </g>
</svg>

座標変換

SVG では座標変換がサポートされています。

図の中心を原点とするには、transform で translate を指定します。

<svg width="50" height="50">
    <g stroke="black" fill="none" transform="translate(25 25)">
        <circle cx="0" cy="0" r="22" />
    </g>
</svg>

scale によってスケールが変換できます。

<svg width="100" height="100">
    <g stroke="black" fill="none" transform="translate(50 50) scale(2)">
        <circle cx="0" cy="0" r="22" />
    </g>
</svg>

translate と scale の順番には意味があります。上の例ではまず座標を平行移動させてからスケールを変換しています。順番を逆にすれば結果も変わります。

<svg width="100" height="100">
    <g stroke="black" fill="none" transform="scale(2) translate(50 50)">
        <circle cx="0" cy="0" r="22" />
    </g>
</svg>

上の例では、まずスケールを 2 倍にしています。次の平行移動で指定した 50 50 は、スケール変換された座標系に基づいています。つまり以下と同じ結果になります。

<svg width="100" height="100">
    <g stroke="black" fill="none" transform="translate(100 100) scale(2)">
        <circle cx="0" cy="0" r="22" />
    </g>
</svg>

scale は縦と横を別々に指定することも可能です。

<svg width="100" height="50">
    <g stroke="black" fill="none" transform="translate(50 25) scale(2 1)">
        <circle cx="0" cy="0" r="22" />
    </g>
</svg>

縦のスケールを -1 倍にすれば、座標の向きを逆にできます。以下の例では、図の中心を原点として、y 軸は上向きとなります。数学のグラフを作るのに適した座標系です。

<svg width="100" height="100">
    <g stroke="black" fill="none" transform="translate(50 50) scale(1 -1)">
        <circle cx="0" cy="0" r="42" />
        <circle cx="30" cy="30" r="10" />
        <circle cx="-30" cy="-30" r="10" />
    </g>
</svg>

回転は rotate です。これを使えば、円上の点の座標を別途計算せずに指定できます。

<svg width="100" height="100">
    <g stroke="black" transform="translate(50 50) scale(1 -1)">
        <circle cx="0" cy="0" r="40" fill="none" />
        <circle cx="40" cy="0" r="2.5" transform="rotate(-30)" />
        <circle cx="40" cy="0" r="2.5" transform="rotate(90)" />
        <circle cx="40" cy="0" r="2.5" transform="rotate(210)" />
    </g>
</svg>

※ transform が使えるのは g タグに限りません。

直線

line タグと path タグのどちらでも描画できます。path タグは高機能で複雑ですが、慣れると便利です。

まず line タグです。

<svg width="100" height="100">
    <g stroke="black" transform="translate(50 50) scale(1 -1)">
        <line x1="-50" y1="0" x2="50" y2="0" />
        <line x1="0" y1="-50" x2="0" y2="50" />
    </g>
</svg>

次に path タグです。

<svg width="100" height="100">
    <g stroke="black" transform="translate(50 50) scale(1 -1)">
        <path d="M -50 0 L 50 0" />
        <path d="M 0 -50 L 0 50" />
    </g>
</svg>

M で描画開始位置を決めて、L まで線を引きます。複数の path をまとめることができます。

<svg width="100" height="100">
    <g stroke="black" transform="translate(50 50) scale(1 -1)">
        <path d="M -50 0 L 50 0 M 0 -50 L 0 50" />
    </g>
</svg>

慣れないと読みにくいかもしれませんが、SVG を短く書くのには有用です。

M や L を小文字にすれば、直前の座標からの相対座標になります。最初の位置は相対化できないので大文字でも小文字でも結果は同じですが、大文字で書いておくのが無難でしょう。

<svg width="100" height="100">
    <g stroke="black" transform="translate(50 50) scale(1 -1)">
        <path d="M -50 0 l 100 0 m -50 -50 l 0 100" />
    </g>
</svg>

座標軸を描画します。回転によって同じ座標が使い回せます。

<svg width="110" height="110">
    <g stroke="black" fill="none" transform="translate(50 60) scale(1 -1)">
        <path d="M -50 0 l 110 0 l -10 -5 m 0 10 l 10 -5" />
        <path d="M -50 0 l 110 0 l -10 -5 m 0 10 l 10 -5" transform="rotate(90)" />
    </g>
</svg>

Z で最初の位置に戻ります。多角形の描画に有用です。

<svg width="100" height="100">
    <g stroke="black" fill="none" transform="translate(50 50) scale(1 -1)">
        <path d="M -45 -45 L 0 45 L 45 -45 Z" />
    </g>
</svg>

文字

text タグを使用します。scale で上下を反転させている中で使用すると文字が逆向きになるため、その外部に記述します。

x y

<svg width="130" height="130">
    <g stroke="black" fill="none" transform="translate(50 80) scale(1 -1)">
        <path d="M -50 0 l 110 0 l -10 -5 m 0 10 l 10 -5" />
        <path d="M -50 0 l 110 0 l -10 -5 m 0 10 l 10 -5" transform="rotate(90)" />
    </g>
    <g fill="black" text-anchor="middle">
        <text x="115" y="85">x</text>
        <text x="50" y="15">y</text>
    </g>
</svg>

fill は文字の色、text-anchor は横の位置揃えです。

楕円

ellipse タグを使用します。中心の座標と(焦点ではありません)、横方向と縦方向の半径を指定します。

<svg width="100" height="50">
    <ellipse cx="50" cy="25" rx="45" ry="22" stroke="black" fill="none" />
</svg>

斜めの楕円は transform を併用します。rotate の 2 番目と 3 番目の数字は、回転の中心です。

<svg width="100" height="100">
    <ellipse cx="50" cy="50" rx="45" ry="22" stroke="black" fill="none"
             transform="rotate(30 50 50)" />
</svg>

弧は角度ではなく、始点と終点と半径などで指定します。

楕円のサイズと向きを固定すれば、ある 2 点を通る楕円は 2 つあります。

<svg width="150" height="120">
    <g stroke="black" fill="none">
        <ellipse cx="0" cy="0" rx="45" ry="22" transform="translate(50 50) rotate(30)" />
        <ellipse cx="0" cy="0" rx="45" ry="22" transform="translate(100 80) rotate(30)" />
    </g>
    <circle cx="84" cy="51" r="2.5" />
    <circle cx="66" cy="79" r="2.5" />
</svg>

この 2 点を始点と終点とする弧は 4 つあります。それらを 2 つのパラメーターで指定します。

<svg width="150" height="120">
    <g fill="none">
        <path d="M 84 51 A 45 22, 30, 0 0, 66 79" stroke="black" />
        <path d="M 84 51 A 45 22, 30, 0 1, 66 79" stroke="gray" />
        <path d="M 84 51 A 45 22, 30, 1 0, 66 79" stroke="red" />
        <path d="M 84 51 A 45 22, 30, 1 1, 66 79" stroke="brown" />
    </g>
    <circle cx="84" cy="51" r="2.5" />
    <circle cx="66" cy="79" r="2.5" />
</svg>

パラメーターは "M 始点X 始点Y A 半径X 半径Y, 角度, 長短 向き, 終点X 終点 Y" です。長短は 1 なら長い方、向きは 1 なら時計回りで、そうでないときは 0 を指定します。

※ 長短と向きをすぐに把握するのは困難なため、とりあえず 0 0 と指定しておいて、想定と違う場合は 1 に変えながら調整すると良いでしょう。

なお、パラメーターを区切るコンマ ',' には特別な意味はなく、空白だけで区切っても構いません。パラメーターがあまりにも多いため、意味の区切りにコンマを入れることによって、多少でも可読性を上げようという工夫です。

※ コンマと空白の使い分けは好みのため、別の流儀もあります。

慣れないうちは弧を描く前に円や楕円を描いて、そこに色を変えた弧を重ねるようにすることをお勧めします。

ここまで説明したタグを使って、三角形の三辺の長さを図示する例を示します。

a c b

<svg width="100" height="100">
    <g stroke="black" fill="none">
        <path d="M  5 80 L 50 2 L 95 80 Z" stroke-width="1.5" />
        <path d="M  5 80 A 80 80, 0, 0 1, 50  2" />
        <path d="M 95 80 A 80 80, 0, 0 0, 50  2" />
        <path d="M  5 80 A 80 80, 0, 0 0, 95 80" />
    </g>
    <g fill="white">
        <circle cx="17" cy="33" r="8" />
        <circle cx="83" cy="33" r="8" />
        <circle cx="50" cy="90" r="8" />
    </g>
    <g text-anchor="middle">
        <text x="17" y="36">a</text>
        <text x="83" y="36">c</text>
        <text x="50" y="98">b</text>
    </g>
</svg>

テキストの回り込み

図が小さい場合、図を右寄せしてテキストを回り込ませるとスペースが節約できます。

テキストの回り込みのテスト テキストの回り込みのテスト テキストの回り込みのテスト テキストの回り込みのテスト

<svg width="80" height="80" style="float: right">
    <circle cx="40" cy="40" r="35" stroke="black" fill="none" />
</svg>

テキストの回り込みのテスト
テキストの回り込みのテスト
テキストの回り込みのテスト
テキストの回り込みのテスト

資料

今回は基本的な事項だけを説明しました。これだけでも簡単な図なら描けると思います。

もっと詳しい機能については、MDN が参考になります。

図形描画ツール

手書きではタグなどを覚える必要もあり、なかなか思ったように図形が表現できずに苦労します。ツールを使用すれば、図形を視覚的に編集することができます。

手書きする方法が分からないときは、ツールで描画して、出力された SVG を確認するのがお手軽です。

有償ソフトでは Adobe 社の Illustrator が有名です。フリーソフトでは Inkscape があります。

ブラウザ上で利用できるツールもあります。以下の SVG-edit は、左上の<svg>ボタンで SVG のソースを直接編集できるため使いやすいです。

ツールを利用すれば、手書きでは難しい表現も可能になります。ただし出力される SVG はそれ相応に複雑になるため、Markdown の中に直接貼るのには適さないかもしれません。必要な部分だけ抜粋して手書きに取り込むなど、運用上の工夫が必要になるでしょう。