【Scratch入門】SVG画像のviewportの意味とviewBox属性の使い方
※ 当ページには【広告/PR】を含む場合があります。
2021/02/14
Scratchで扱える基本的な画像で、
一長一短がありどちらの形式の方が優れているとはいえないのですが、Scratch3になってからはブラウザ駆動のSPA(Single-Page-Application)としてどのようなブラウザーからも動作するようになりました。
これにより、Webと親和性の高いsvg画像を積極的に利用していくほうが、Scratchゲーム開発の応用も捗っていくことと思います。
今回はScratchアプリの基礎講座としてのsvg画像の取扱で重要な
SVGを使って描画位置を操作する
SVG画像には
viewBox
Viewport
もし動的にcanvas要素の中で画像を操作したい場合に、pngやjpgのようなラスター画像を
同じJavascriptプログラミングでも、SVG画像をベースにcanvas要素で操作するならば、こちらの方が直感的な使い方だけで十分に事足りるので、あまりコードの実装に時間をかけたくないプログラマーの方にオススメです。
SVGの使い方でもっとも分かりづらくつまずきやすいのが、
Viewport
viewBox
今回はこの
Viewport
viewBox
ViewportとviewBox
個別に具体的な画像を使いながらテストしていきます。
なお、この
ビューポートはviewBoxで操作する
この首題からも分かるように、viewportという言葉とviewboxという言葉が本来は同じ意味合いを指しているはずなのに、viewportかviewboxのどっちかの単語で統一出来なかったものか...という気もします。
SVGの画像規格を決める方の何かの強い思い入れでもあったのか、ビューポートはcanvas要素内のsvg画像が表示される描画範囲を表す単語で、viewBoxはsvg要素のビューポートを設定するためのアトリビュート名、という位置づけで落ち着いているようです。
オブジェクト指向的な理解では、svgというクラスがあったなら、viewportというプライベートなメンバー変数があり、そのviewportに変更を加えるのが、viewboxというメソッドになる...ようなイメージでしょうか。
言葉遊びはさておき、svg画像を操作したいScratch(Javascript)プログラマーが知っておくことは、
svg画像はviewBox属性で範囲を調整する
ということでviewBoxの使い方と作用を以下で詳しく見ていこうと思います。
viewBoxの使い方
まずは以下のような300x300(px)のSVG画像で黒の円(中心(150,150)の半径100)があるとします。 まだここでは
viewBox
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300">
<circle cx="150" cy="150" r="100" fill="#000000" />
</svg>
これを描くと、描画の諸元はシステムのデフォルトで表示されます。

この画像に
viewBox
ちなみにviewBox属性の用法は、
viewBox="x1 y1 width height"
x1: canvas要素の左上を絶対原点(0,0)としたときの、
ビューポートの相対的な原点のx座標値
進行右方向が正と定める。
y1: canvas要素の左上を絶対原点(0,0)としたときの、
ビューポートの相対的な原点のy座標値
進行下方向が正と定める。
width:
ビューポートの描画幅。
幅はビューポートの左上相対原点のx1からの右端までの長さになる。
height:
ビューポートの描画高さ(座標値)
高さはビューポートの左上相対原点のy1からの下端までの長さになる。
というふうに4つの数を使って記述します。
まず
viewBox="0 0 300 300"
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
<circle cx="150" cy="150" r="100" fill="#000000" />
</svg>

当然ながら、
canvasサイズ = ビューポート
次に
viewBox="150 150 300 300"
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="150 150 300 300">
<circle cx="150" cy="150" r="100" fill="#000000" />
</svg>

ビューポートを
viewBox="150 150 300 300"
(150,150)
またビューポートの幅と高さは300のままですので、先程のsvg画像の大きさはそのままに、黒の円だけが左上にxy座標で150ずつ並行移動したような図になっています。
さて、この図を見ると円を並行移動したためsvg画像に大分無駄な余白が空いてしまいました。 この部分が要らないので、ビューポートの範囲(幅と高さ)を縮めて
viewBox="150 150 200 200"
つまりは
canvasサイズ(の幅/高さ) > ビューポート(の幅/高さ)
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="150 150 200 200">
<circle cx="150" cy="150" r="100" fill="#000000" />
</svg>
このsvg画像を試すと、以下のようになります。

ビューポートの操作に慣れていない方は「あれっ...少し拡大されてデカくない?」と描画された結果に違和感を感じると思います。
canvas(の幅/高さ) > ビューポート(の幅/高さ)
そもそも何故画像が拡大したかというと、
canvas(の幅/高さ) > ビューポート(の幅/高さ)
svg画像のビューポートの性質上、この例のようにビューポートのサイズが描画されるcanvasのサイズより小さい場合は、その空きのある描画スペースいっぱいにsvg画像を敷き詰めるように引き伸ばされます。
よって引き伸ばされる倍率は
ビューポート伸縮倍率 = canvas(の幅or高さ) / ビューポート(の幅or高さ)
ですので今回の例でいうと、
300 / 200 = 1.5
ではちなみに
canvas(の幅/高さ) < ビューポート(の幅/高さ)
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="150 150 600 600">
<circle cx="150" cy="150" r="100" fill="#000000" />
</svg>

というように、今度は
300 / 600 = 0.5
ここで得られる教訓として、svg画像を等倍に扱いたい場合は
canvas(の幅/高さ) = ビューポート(の幅/高さ)
preserveAspectRatio
先にビューポートの基本的な働きを確認するために、
ビューポートの幅 = ビューポートの高さ
preserveAspectRatio
この
何も設定しない場合、デフォルト値は
"xMinYMin meet"
これが設定されていると、
canvas(の幅/高さ) > ビューポート(の幅/高さ)
例えば上の例で
viewBox="0 0 200 300"
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 200 300">
<circle cx="150" cy="150" r="100" fill="#000000" />
</svg>

これはビューポートをcanvasと等倍(
viewBox="0 0 200 300"
これが
preserveAspectRatio="xMinYMin meet"
100 / 2 = 50

では最後に
ビューポートの幅 ≠ ビューポートの高さ
canvas(の幅/高さ) < ビューポート(の幅/高さ)
これは上記までのビューポートの再描画ルールの合わせ技になるので、何が起こったのか極めて混乱しやすいケースになります。
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 600 300">
<circle cx="150" cy="150" r="100" fill="#000000" />
</svg>

たかだかビューポートを
viewBox="0 0 600 300"
何が起こったのか焦らないで上で説明したことを順当に当てはめていくと、ビューポートの幅がcanvasよりも長いのでまず
canvas幅 / ビューポート幅 = 300 / 600 = 0.5
preserveAspectRatio="xMinYMin meet"
さらにこの縮小後のビューポートサイズ(300x150)になってしまうので、canvasのサイズ(300x300)と比べて下の空間が(300x150)の領域だけ空いてしまいます。
そこでその不足空間を埋めるように中央寄せされる時に、下方向へ
150 / 2 = 75
...とまぁ理屈は難解ですが、
canvas(の幅/高さ) < ビューポート(の幅/高さ)
canvas(の幅/高さ) >= ビューポート(の幅/高さ)
まとめ
今回はScratchアプリで頻繁に利用するsvg画像の中でも重要な設定であるビューポート(viewBox)とcanvas再描画時の更新ルールを決めるpreserveAspectRatioに関して、基礎的な使い方を検証してみました。
svgのビューポートの概念に慣れてしまえば意外になんとも不思議に思わなくなって来るのですが、svgを使い始めた方からすると混乱必至な内容になります。
是非理解できるまでじっくりsvg画像とにらめっこして自身の納得のいくまで考えてみてください。