SVGには、基本的な図形を描くタグがあらかじめ用意されている。直線も曲線も描ける<path>
タグは、それらの中で最も自由度が高い。そのぶん使い方は複雑だが、要点を押えればあらゆる図形を<path>
タグだけで表現できる。
さぞ多彩な属性値があるかと思いきや、同タグ特有の属性値は以下の2つだけである(もちろんfill
やstroke
などの一般的なSVG属性も指定できる)。
属性名 | 概要 |
---|---|
d | "Path Data"の略。パスの形を定義するための文字列。 |
pathLength | パスの長さを定義するための数値。デフォルトはnone で、パスの長さがそのまま描画される。 |
このうち、曲者はd
属性である。実用的な図形を描くためには、呪文のようなアルファベットと数字の羅列を長々と指定する必要がある。この文字列は、線の種類を示すコマンドとコマンドに渡されるパラメータの連なりである。
コマンドの書式
W3Cは、d
属性に指定できる値について、以下のように定めている。
- すべてのコマンドは1文字で表現される
- 区切り文字には空白あるいはを使う
- 余分な空白やカンマなどの区切り記号は省くことができる
- 同じコマンドが連続する場合、最初の1回以外は省略できる
- すべてのコマンドで絶対座標と相対座標を利用できる(前者は大文字、後者は小文字)
- 読みやすさ向上のために、
d
属性には改行を含めることができる
ちなみにSVGの座標系では、左上の角が原点
<path>
タグのコマンドは、コマンドの種類を示すアルファベット1文字と、座標を示す単位のない数値によって構成される。区切り文字は2種類使え、いずれも同じ意味を持ち、かつ省略できる。
そのため、書き手(あるいはデザインツールの作り手)の好みよって幾つかのバリエーションがある。
ご覧の通り、どの方法を用いてもパッと見で図形の全容を把握するのは激ムズである。
ただ、コーディングをしていると<path>
を読み解かなければならないことが稀によくある。大枠だけでも掴んでおけば、のちのち役に立つ。そう信じて前に進む。
W3CやMDNのページでは、空白スペースで区切る形が使われている。これはコマンドが長くなると分かりづらいため、本稿では
なお、d
属性の詳細な文法については、先に参照したページの「8.3.9 The grammar for path data」の項にバッカス・ナウア記法で記されている。
コマンドの種類
d
属性で使うコマンドの種類は、直線にかんするコマンドと曲線にかんするコマンドに大別される。
直線にかんするコマンド
直線にかんするコマンドの種類は、さらに以下の4つに大別される。大文字と小文字の違いは、パラメータに渡す座標の原点がviewBox
の原点(左肩)か、直前の終点か、というだけである。
M
とm
M
は、d
属性に必ず指定する必要のあるコマンドである。個人的には、原点をデフォルトにしてもええんじゃないかと思うが、なぜか省略すると線が引かれない。
また、M
属性だけを指定してもやはり線は描画されない。L
なりH
なりV
なり、線を描画するコマンドとセットで描画する必要がある。
コマンド名 | 概要 |
---|---|
M | "Move To"の略。線の開始位置を与えられた絶対座標に移動する。 |
m | 直前の点を基準に、線の開始位置を与えられた相対座標に移動する。 |
L
とl
コマンド名 | 概要 |
---|---|
L | "Line To"の略。直前の点から、与えられた絶対座標に線を引く。 |
l | 直前の点から、与えられた相対座標に線を引く。 |
以下のサンプルコードに含まれる2つの<path>
タグでは、まずM 50 50
で始点を矩形の中央に移動し、その後にL
コマンドとl
コマンドにそれぞれ同じ座標を指定して両者の違いを示す。L
が青線でl
が赤線である。
L
コマンドでは、始点である(50,50)から(50,25)に向かって直線が引かれている。一方のl
コマンドでは、始点の(50,50)に(50,25)が差分として足され、(100,75)に向かって直線が引かれていることがわかる。
H
とh
とV
とv
座標を連続して書くと、直前の点から垂直、あるいは水平の線を引く場合に記述が冗長になる。これを簡略化して書けるのがH
(h
)コマンドとV
(v
)コマンドである。
コマンド名 | 概要 |
---|---|
H | "Horizontal Line To"の略。直前の点から、与えられたx軸の絶対座標に向けて線を引く。 |
h | 直前の点から、与えられたx軸の相対座標に向けて線を引く。 |
V | "Vertical Line To"の略。直前の点から、与えられたy軸の絶対座標に向けて線を引く。 |
h | 直前の点から、与えられたy軸の相対座標に向けて線を引く。 |
先のサンプルと同様に、2つのH
コマンドとh
コマンドを指定する。
棒磁石みたいになってしまった。
V
とv
も、ほとんど同じ使い心地である。
Z
Z
も、L
コマンドを簡略に書くためのコマンドである。座標を指定する必要はなく、Z
の一文字で終点から始点を結ぶ線を引いてくれる。
コマンド名 | 概要 |
---|---|
Z | "Close Path"の略。直前の点と線の始点を結ぶ線を引く。 |
z | 大文字と小文字に違いはない。 |
ちなみにZ
コマンドを使った後でも、コマンドは続けることができる。
曲線にかんするコマンド
曲線にかんするコマンドは、ベジエ曲線を用いるものと円弧を用いるものに大別される。
ベジエ曲線とは、計算によって描く、任意の2点を結ぶ滑らかな曲線である。この計算方法は、1960年代にピエール・ベジエというフランスのエンジニアが実用化した。
ベジエ曲線では、制御点という点を動かして線のカーブをコントロールする。制御点は、線を引っ張る重力を持った不思議な点、とでも考えれば良い(がっちり理解したければ、線形補完を勉強すると良い)。
制御点(始点と終点も含まれる)の個数が
SVGのd
属性では、これらのうち2次ベジエ曲線と3次ベジエ曲線をサポートしている。あと円弧。各コマンドは以下の通りである。
Q
とq
とT
とt
2次ベジエ曲線は、3つの制御点によって描かれる。
まず始点と制御点を結ぶ直線上、そして制御点と終点を結ぶ直線上で、それぞれ同じ割合で点を移動する。次に、これらの2つの点を直線で結ぶ。
この直線上に点を置き、先の2つの点と同じ割合で移動させると、その移動する点の軌跡が求める2次ベジエ曲線となる。
例えば始点、制御点、終点を等距離に、正三角形を描くように配置すると、三角形の高さの半分に曲線の頂点が来る。この事実をうっすらイメージしておくと、以下のコマンドの働きがわかりよいはずである。
コマンド名 | 概要 |
---|---|
Q | "Quadratic Bézier curve"の略。絶対座標で制御点と終点を受け取り、終点への曲線を引く。 |
q | 相対座標で制御点と終点を受け取り、始点から終点への曲線を引く。 |
T | Smoothの略(たぶん)。1つ前の制御点を、直前の点を中心にした点対称の位置に置き、雑多い座標で渡された終点に向けて曲線を引く。 |
t | T コマンドの相対座標版。 |
まずQ
コマンドとq
コマンドの働きを見る。
以下は、それぞれのコマンドを2つ続け、2つのカーブを持つ曲線を描いている。わかりやすいように、始点と終点を含む各制御点のq
コマンドは先に下端、後に上端)それぞれ設定している。
Q
コマンドもq
コマンドも、制御点を結ぶ三角形の高さの半分(
続いてT
コマンドとt
コマンドの働きを見る。先のQ
コマンドとq
コマンドのサンプルコードは、それぞれの制御点を点対称に配置している。これをそのままT
コマンドとt
コマンドに置き換える。
同じ図形が描かれていることがわかる。
S
とs
とC
とc
2次ベジエ曲線は、3つ制御点によって描かれる。3次ベジエ曲線は、これにもう1つ制御点が加わったものである。
コマンド名 | 概要 |
---|---|
C | "Cubic Bézier curve"の略。絶対座標で3つの制御点を受け取り、最後の制御点に向けて曲線を引く。 |
c | 相対座標で3つの制御点を受け取り、最後の制御点に向けて曲線を引く。 |
S | Smoothの略(たぶん)。1つ前の2つの制御点を、直前の点を中心にした点対称の位置に置き、渡された終点に向けて曲線を引く。 |
s | S コマンドの相対座標版。 |
まずはC
コマンドとc
コマンドのサンプルを示す。それぞれ、2つのコマンドを点対称に繋げている。また、各コマンドの制御点は、正四角形になるように配置している。
S
コマンドは、1つ前のコマンドの制御点(終点の1つ前)の点対称に制御点を置く。上のサンプルコードと下のサンプルコードを見比べると、後者では2つ目のコマンドの最初の制御点が省略されていることがわかる。
A
とa
円弧を扱うA
コマンドは、ベジエ曲線よりも扱いが簡単に思える(そんな感じのことが<path>
タグに関するMDNの解説ページにも書いてある)。
個人的な感覚では、A
コマンドの方がクセが強く、理解するのに難儀した。
コマンド名 | 概要 |
---|---|
A | "Elliptical Arc Curves"の略。後述するパラメータに基づいて、一意に定められた円弧を描画する。 |
a | A コマンドの相対座標版。 |
A
コマンドは、以下のパラメータを取る。
パラメータ名 | 概要 |
---|---|
rx | 楕円の水平方向の半径。 |
ry | 楕円の垂直方向の半径。 |
x-axis-rotation | 楕円の傾き。 |
large-arc-flag | 円弧の大きい方か小さい方かを判定するフラグ。大きい方は1 、小さい方は0 。 |
sweep-flag | 円弧を始点から時計回りに描くか、反時計回りに描くか。時計回りは1 、反時計回りは0 。 |
x | 終点の |
y | 終点の |
なお、指定された半径rx
およびry
が始点と終点の間の距離に対して大きすぎるか小さすぎる場合、描画に使われる楕円の半径は自動的に調整される。
以下のサンプルでは、始点と終点の距離を短径、その倍の長さを長径に指定した楕円の円弧を使って、これらのパラメータの働きを確かめる。
左右2つとも、large-arc-flag
とsweep-flag
の組み合わせ(4パターン)を網羅する形でパスを指定している。円弧を時計回りに描くか、反時計回りで描くかを指定するsweep-flag
を同じ図形に対して両方指定しているため、完全な楕円が描かれている。
ただ、左側は2つの円弧(薄青とピンク)しか描かれていない。これは、1つ目のパスの上に3つ目のパスが、2つ目のパスの上に4つ目のパスが重なっているためである。
始点と終点によって、楕円は2つの円弧に分けられる。large-arc-flag
は、このうちの大きい方を描くか小さい方を描くかを指定する。
しかし、例えば楕円に角度がついていなかったり、楕円が真円になっていたりすると、始点と終点で2つに分けた円弧の大きさが変わらない。こういった状況でlarge-arc-flag
を指定しても、図形は変わらない。
右は、左側のパスをコピーし、x-axis-rotation
に60を指定した(60度傾けた)ものである。こちらは、large-arc-flag
の働きがよくわかる。
x-axis-rotation
は、楕円の