アフィン変換で図形パターンを量産する。
前回まで
今回
前回作成した矢印を流用して複数の矢印図形を作ってみる。
成果物
- インライン版
- 外部参照版
インライン版
サーバ不要。
index.html
<meta charset="UTF-8"> <svg xmlns="http://www.w3.org/2000/svg" style="display:none;"> <defs> <symbol id="arrow-top" viewBox="0 0 256 256"> <path style="opacity:1;fill:none;fill-opacity:0;stroke:currentColor;stroke-width:16;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M128 7.781 7.781 120.22H64V248h128V120.219h56.219L128 7.78z"/> </symbol> <symbol id="arrow-bottom" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(180, 128, 128)"></use></symbol> <symbol id="arrow-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(270, 128, 128)"></use></symbol> <symbol id="arrow-right" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(90, 128, 128)"></use></symbol> <symbol id="arrow-top-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(315, 128, 128)"></use></symbol> <symbol id="arrow-top-right" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(45, 128, 128)"></use></symbol> <symbol id="arrow-bottom-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(225, 128, 128)"></use></symbol> <symbol id="arrow-bottom-right" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(135, 128, 128)"></use></symbol> <symbol id="arrow-top-bottom" viewBox="0 0 256 256"> <use href="#arrow-top" transform=""></use> <use href="#arrow-bottom" transform=""></use> </symbol> <symbol id="arrow-pair-top-bottom" viewBox="0 0 256 256"> <use href="#arrow-top" transform="scale(0.5, 1)"></use> <use href="#arrow-bottom" transform="scale(0.5, 1) translate(256, 0)"></use> </symbol> <symbol id="arrow-pair-left-right" viewBox="0 0 256 256"> <use href="#arrow-left" transform="scale(1, 0.5)"></use> <use href="#arrow-right" transform="scale(1, 0.5) translate(0, 256)"></use> </symbol> <symbol id="arrow-quad-outside" viewBox="0 0 256 256"> <use href="#arrow-top-left" transform="scale(0.5, 0.5)"></use> <use href="#arrow-top-right" transform="scale(0.5, 0.5) translate(256, 0)"></use> <use href="#arrow-bottom-left" transform="scale(0.5, 0.5) translate(0, 256)"></use> <use href="#arrow-bottom-right" transform="scale(0.5, 0.5) translate(256, 256)"></use> </symbol> <symbol id="arrow-quad-inside" viewBox="0 0 256 256"> <use href="#arrow-bottom-right" transform="scale(0.5, 0.5)"></use> <use href="#arrow-top-right" transform="scale(0.5, 0.5) translate(0, 256)"></use> <use href="#arrow-bottom-left" transform="scale(0.5, 0.5) translate(256, 0)"></use> <use href="#arrow-top-left" transform="scale(0.5, 0.5) translate(256, 256)"></use> </symbol> </defs> </svg> <style> .icon{width:1em; height:1em;} </style> <svg class="icon"><use href="#arrow-top"></use></svg> <svg class="icon"><use href="#arrow-bottom"></use></svg> <svg class="icon"><use href="#arrow-left"></use></svg> <svg class="icon"><use href="#arrow-right"></use></svg> <svg class="icon"><use href="#arrow-top-left"></use></svg> <svg class="icon"><use href="#arrow-top-right"></use></svg> <svg class="icon"><use href="#arrow-bottom-left"></use></svg> <svg class="icon"><use href="#arrow-bottom-right"></use></svg> <svg class="icon"><use href="#arrow-pair-top-bottom"></use></svg> <svg class="icon"><use href="#arrow-pair-left-right"></use></svg> <svg class="icon"><use href="#arrow-quad-outside"></use></svg> <svg class="icon"><use href="#arrow-quad-inside"></use></svg> <svg class="icon"><use href="#arrow-top" transform="rotate(15, 8, 8)" fill="red"></use></svg> <svg class="icon"><use href="#arrow-top" style="color:red;"></use></svg> <svg class="icon"><use href="#arrow-top" style="color:green;"></use></svg> <svg class="icon"><use href="#arrow-top" style="color:blue;"></use></svg> <svg class="icon"><use href="#arrow-top" style="color:yellow;"></use></svg> <svg class="icon"><use href="#arrow-top" style="color:magenta;"></use></svg> <svg class="icon"><use href="#arrow-top" style="color:cyan;"></use></svg> <svg width="2em" height="2em"><use href="#arrow-top"></use></svg> <svg width="3em" height="3em"><use href="#arrow-top"></use></svg> <svg width="4em" height="4em"><use href="#arrow-top"></use></svg> <svg width="5em" height="5em"><use href="#arrow-top"></use></svg>
外部参照版
arrows.svg
<svg xmlns="http://www.w3.org/2000/svg" style="display:none;"> <defs> <symbol id="arrow-top" viewBox="0 0 256 256"> <path style="opacity:1;fill:none;fill-opacity:0;stroke:currentColor;stroke-width:16;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M128 7.781 7.781 120.22H64V248h128V120.219h56.219L128 7.78z"/> </symbol> <symbol id="arrow-bottom" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(180, 128, 128)"></use></symbol> <symbol id="arrow-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(270, 128, 128)"></use></symbol> <symbol id="arrow-right" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(90, 128, 128)"></use></symbol> <symbol id="arrow-top-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(315, 128, 128)"></use></symbol> <symbol id="arrow-top-right" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(45, 128, 128)"></use></symbol> <symbol id="arrow-bottom-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(225, 128, 128)"></use></symbol> <symbol id="arrow-bottom-right" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(135, 128, 128)"></use></symbol> <symbol id="arrow-top-bottom" viewBox="0 0 256 256"> <use href="#arrow-top" transform=""></use> <use href="#arrow-bottom" transform=""></use> </symbol> <symbol id="arrow-pair-top-bottom" viewBox="0 0 256 256"> <use href="#arrow-top" transform="scale(0.5, 1)"></use> <use href="#arrow-bottom" transform="scale(0.5, 1) translate(256, 0)"></use> </symbol> <symbol id="arrow-pair-left-right" viewBox="0 0 256 256"> <use href="#arrow-left" transform="scale(1, 0.5)"></use> <use href="#arrow-right" transform="scale(1, 0.5) translate(0, 256)"></use> </symbol> <symbol id="arrow-quad-outside" viewBox="0 0 256 256"> <use href="#arrow-top-left" transform="scale(0.5, 0.5)"></use> <use href="#arrow-top-right" transform="scale(0.5, 0.5) translate(256, 0)"></use> <use href="#arrow-bottom-left" transform="scale(0.5, 0.5) translate(0, 256)"></use> <use href="#arrow-bottom-right" transform="scale(0.5, 0.5) translate(256, 256)"></use> </symbol> <symbol id="arrow-quad-inside" viewBox="0 0 256 256"> <use href="#arrow-bottom-right" transform="scale(0.5, 0.5)"></use> <use href="#arrow-top-right" transform="scale(0.5, 0.5) translate(0, 256)"></use> <use href="#arrow-bottom-left" transform="scale(0.5, 0.5) translate(256, 0)"></use> <use href="#arrow-top-left" transform="scale(0.5, 0.5) translate(256, 256)"></use> </symbol> </defs> </svg>
パスデータがあるのは最初の図形id="arrow-top"
だけ。他はそれを<use>
で参照し、transform
でアフィン変換したものを流用しているだけ。
index.html
<meta charset="UTF-8"> <style> .icon{width:1em; height:1em;} </style> <svg class="icon"><use href="./arrows.svg#arrow-top"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-bottom"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-left"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-right"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top-left"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top-right"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-bottom-left"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-bottom-right"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-pair-top-bottom"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-pair-left-right"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-quad-outside"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-quad-inside"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top" transform="rotate(15, 8, 8)" fill="red"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top" style="color:red;"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top" style="color:green;"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top" style="color:blue;"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top" style="color:yellow;"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top" style="color:magenta;"></use></svg> <svg class="icon"><use href="./arrows.svg#arrow-top" style="color:cyan;"></use></svg> <svg width="2em" height="2em"><use href="./arrows.svg#arrow-top"></use></svg> <svg width="3em" height="3em"><use href="./arrows.svg#arrow-top"></use></svg> <svg width="4em" height="4em"><use href="./arrows.svg#arrow-top"></use></svg> <svg width="5em" height="5em"><use href="./arrows.svg#arrow-top"></use></svg>
HTTPS server
解説
SVGスプライトを作る。フォーマットは以下。
<svg> <defs> <symbol id="..."> <path d="..."/> </symbol> </defs>
元となる矢印の図形はInkscapeで矢印を描くの成果物を使う。すると以下のようになる。
<symbol id="arrow-top" viewBox="0 0 256 256"> <path style="opacity:1;fill:none;fill-opacity:0;stroke:currentColor;stroke-width:16;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M128 7.781 7.781 120.22H64V248h128V120.219h56.219L128 7.78z"/> </symbol>
あとは上記をアフィン変換して流用する。上記は上向きの矢印だが、反対に下向きにするため回転させる。
<symbol id="arrow-bottom" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(180, 128, 128)"></use></symbol>
ポイントはtransform
属性値。ここにrotate(角度, 中点X, 中点Y)
を入力する。角度は0
〜360
、中点は回転する中心点をXとYの座標で指定する。単位はpx(ピクセル)。今回の図形はviewBox
にある通り幅と高さがそれぞれ256
px。画面中央で回転させたいから、(128, 128)
を中点として指定した。transform="rotate(180, 128, 128)"
になる。
あとはこれを上下左右斜めの八方向だけ用意する。
複数の図形を合わせたパターンもある。以下は上向きと下向きの矢印を半分のサイズにして一つの図形にしたもの。
<symbol id="arrow-pair-top-bottom" viewBox="0 0 256 256"> <use href="#arrow-top" transform="scale(0.5, 1)"></use> <use href="#arrow-bottom" transform="scale(0.5, 1) translate(256, 0)"></use> </symbol>
四つ合わせて以下のようなものも作れる。
<symbol id="arrow-quad-outside" viewBox="0 0 256 256"> <use href="#arrow-top-left" transform="scale(0.5, 0.5)"></use> <use href="#arrow-top-right" transform="scale(0.5, 0.5) translate(256, 0)"></use> <use href="#arrow-bottom-left" transform="scale(0.5, 0.5) translate(0, 256)"></use> <use href="#arrow-bottom-right" transform="scale(0.5, 0.5) translate(256, 256)"></use> </symbol>
どこまで変更できるのか。じつは以下のような制約がある。
<path>
ですでに設定済みのスタイルは変更不可
use要素の振る舞い
use要素に設定したスタイル属性値はコピー元の図形に値が設定されていない時のみ有効となる
svg要素の基本的な使い方まとめ
よって上記の場合、fill
やstroke
等は<use>
で変更できない。すでに定義済みのスタイルだから。
逆にwidth
,height
,transform
等は未定義のため変更可能。
また、stroke:currentColor
にしているため、CSSのcolor
属性値と同じ値になる。よってCSSで色の変更が可能である。以下は赤色に変更したもの。
<svg class="icon"><use href="#arrow-top" style="color:red;"></use></svg>
こうした抜け道を使えば単色なら変更可能だ。細かく制御したいなら<path>
やclass
属性値等を駆使することになりそう。
サイズに関しては簡単だ。<use>
する側の<svg>
でwidth
,height
を指定するだけ。
<svg width="5em" height="5em"><use href="#arrow-top"></use></svg>
ただ、定義側で<symbol>
毎にviewBox
を定義するのが冗長に見える。でもこれを省略したり、親の<svg>
や<defs>
だけに指定しても正しく表示されなかった。毎回<symbol>
にviewBox
をセットする必要があるようだ。
どうせ全部同値のviewBox="0 0 256 256"
なのだから一度だけ書いて済ませたかった……。
... <symbol id="arrow-bottom" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(180, 128, 128)"></use></symbol> <symbol id="arrow-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(270, 128, 128)"></use></symbol> <symbol id="arrow-bottom" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(180, 128, 128)"></use></symbol> <symbol id="arrow-left" viewBox="0 0 256 256"><use href="#arrow-top" transform="rotate(270, 128, 128)"></use></symbol> ...
いずれにせよ、スプライト化によりファイルサイズ短縮できた。全パターンをd
属性値でパスデータ設定したら、もっと大きなサイズになっていただろう。
めでたし めでたし。