【Scratch入門】SVG画像のviewportの意味とviewBox属性の使い方


※ 当ページには【広告/PR】を含む場合があります。
2021/02/14
【Scratch入門】簡単なストップウォッチを作ってみる
【Scratch入門】2つの変数(グローバル変数とローカル変数)について考える

Scratchで扱える基本的な画像で、
「ラスター画像」の代表的なものがpng、「ベクトル画像」ではsvgとなります。

一長一短がありどちらの形式の方が優れているとはいえないのですが、Scratch3になってからはブラウザ駆動のSPA(Single-Page-Application)としてどのようなブラウザーからも動作するようになりました。

これにより、Webと親和性の高いsvg画像を積極的に利用していくほうが、Scratchゲーム開発の応用も捗っていくことと思います。

今回はScratchアプリの基礎講座としてのsvg画像の取扱で重要な
ビューポート(Viewport)の概念と使い方の基礎を説明していきます。


合同会社タコスキングダム|タコキンのPスクール【Pschool厳選】Scratchを学べるオンライン・駅前プログラミングスクール5選

SVGを使って描画位置を操作する

SVG画像にはviewBoxアトリビュートを使ってViewportの範囲・位置を簡単に制御できる方法が備わっています。

もし動的にcanvas要素の中で画像を操作したい場合に、pngやjpgのようなラスター画像を
Canvas APIの組込関数を駆使するとかなり複雑なことができるのですが、使いこなしのかなり難しいAPIですので、それなりのラーニングコストが高いことが欠点です。

同じJavascriptプログラミングでも、SVG画像をベースにcanvas要素で操作するならば、こちらの方が直感的な使い方だけで十分に事足りるので、あまりコードの実装に時間をかけたくないプログラマーの方にオススメです。

SVGの使い方でもっとも分かりづらくつまずきやすいのが、
ViewportviewBoxの意味合いなのですが、これらの言葉じりを正しく知ることで、Scratchアプリ開発の質もぐっと高まるはずです。

今回はこの
ViewportviewBoxを詳しく見ていきます。


合同会社タコスキングダム|タコキンのPスクール【Pschool厳選】Scratchをしっかり学ぶためのオススメ書籍まとめ

ViewportとviewBox

個別に具体的な画像を使いながらテストしていきます。

なお、この
SVG画像を表示させていただけるこのツールサイトを使わせていただきながら確認を行っております。

ビューポートは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>
        
これを描くと、描画の諸元はシステムのデフォルトで表示されます。

合同会社タコスキングダム|タコキンのPスクール

この画像に
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>
        

合同会社タコスキングダム|タコキンのPスクール

当然ながら、
canvasサイズ = ビューポートになるようにviewBoxを設定しているので、これと言って変化はありません。

次に
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>
        
合同会社タコスキングダム|タコキンのPスクール

ビューポートを
viewBox="150 150 300 300"としたので、表示するsvg画像のビューポート原点がcanvas要素の座標位置(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画像を試すと、以下のようになります。

合同会社タコスキングダム|タコキンのPスクール

ビューポートの操作に慣れていない方は「あれっ...少し拡大されてデカくない?」と描画された結果に違和感を感じると思います。

canvas(の幅/高さ) > ビューポート(の幅/高さ)になるようにviewBoxをだけを弄っただけなので、'大は小を兼ねる'ような感じで画像の表示サイズ自体は変わらないのでは、と想像していた方もいらっしゃると思いますが、実際にはsvg画像が大きく見えております。元のsvg図形をここでは半径100の円で指定していたのに、これでは半径150の円です。

そもそも何故画像が拡大したかというと、
canvas(の幅/高さ) > ビューポート(の幅/高さ)にしたことが原因です。

svg画像のビューポートの性質上、この例のようにビューポートのサイズが描画されるcanvasのサイズより小さい場合は、その空きのある描画スペースいっぱいにsvg画像を敷き詰めるように引き伸ばされます。

よって引き伸ばされる倍率は

            
            ビューポート伸縮倍率 = canvas(の幅or高さ) / ビューポート(の幅or高さ)
        
ですので今回の例でいうと、300 / 200 = 1.5倍になった、というのはsvgのデフォルトのビューポートの振る舞いだったのです。

ではちなみに
canvas(の幅/高さ) < ビューポート(の幅/高さ)というようにビューポートの方が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>
        

合同会社タコスキングダム|タコキンのPスクール

というように、今度は
300 / 600 = 0.5倍で、もとのsvgサイズから半分の大きさに見えるように縮小されて表示されてしまいました。

ここで得られる教訓として、svg画像を等倍に扱いたい場合は
canvas(の幅/高さ) = ビューポート(の幅/高さ)を気にかけておく必要があるということです。

preserveAspectRatio

先にビューポートの基本的な働きを確認するために、ビューポートの幅 = ビューポートの高さとなるように変化させましたが、ビューポートの理解に欠かせないpreserveAspectRatioという裏設定の属性の説明を後回しにしていました。

この
preserveAspectRatio属性は、ビューポートがcanvas要素とサイズが異なる場合に、どのようにsvg画像の縦横比で変形させるかを設定するためのものです。

何も設定しない場合、デフォルト値は
"xMinYMin meet"となります。

これが設定されていると、
canvas(の幅/高さ) > ビューポート(の幅/高さ)になったときに、ビューポートの幅か高さで小さい方の値を使って、縦横比を変えずにsvg画像を中央寄せするルールが適用されます。

例えば上の例で
viewBox="0 0 200 300"にしてビューポートの幅だけを200に縮めてみます。

            
            <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>
        

合同会社タコスキングダム|タコキンのPスクール

これはビューポートをcanvasと等倍(
viewBox="0 0 200 300")に設定したときと比べて、右に50だけ並行移動したように見えます。

これが
preserveAspectRatio="xMinYMin meet"の移動ルールで、ビューポートがcanvasサイズの空き(今回は右側の100x300の領域)を埋めるために、中央寄せをした結果、100 / 2 = 50の分だけ右に移動しているように修正されていると理解できます。

合同会社タコスキングダム|タコキンのPスクール

では最後に
ビューポートの幅 ≠ ビューポートの高さかつ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>
        
合同会社タコスキングダム|タコキンのPスクール

たかだかビューポートを
viewBox="0 0 600 300"と変えただけなのに、もとのsvg画像とは全く別物になってしまって、svg画像を覚えたての頃などはどういうことなのか分からずかなり焦ります。

何が起こったのか焦らないで上で説明したことを順当に当てはめていくと、ビューポートの幅がcanvasよりも長いのでまず
canvas幅 / ビューポート幅 = 300 / 600 = 0.5倍にx方向に縮小され、preserveAspectRatio="xMinYMin meet"で、縦横比固定なのでx方向も同様の倍率で縮小されます。

さらにこの縮小後のビューポートサイズ(300x150)になってしまうので、canvasのサイズ(300x300)と比べて下の空間が(300x150)の領域だけ空いてしまいます。

そこでその不足空間を埋めるように中央寄せされる時に、下方向へ
150 / 2 = 75だけ平行移動しており、結果y方向だけは中央の位置にいるように見えています。

...とまぁ理屈は難解ですが、
canvas(の幅/高さ) < ビューポート(の幅/高さ)にして使うユースケースはさして思い当たらないので、viewBoxを使うときには通常はcanvas(の幅/高さ) >= ビューポート(の幅/高さ)と予め使用制限をしておいた方が利用者は幸せになれるかも知れません。


合同会社タコスキングダム|タコキンのPスクール【Pschool厳選】Scratchをしっかり学ぶためのオススメ書籍まとめ

まとめ

今回はScratchアプリで頻繁に利用するsvg画像の中でも重要な設定であるビューポート(viewBox)とcanvas再描画時の更新ルールを決めるpreserveAspectRatioに関して、基礎的な使い方を検証してみました。

svgのビューポートの概念に慣れてしまえば意外になんとも不思議に思わなくなって来るのですが、svgを使い始めた方からすると混乱必至な内容になります。

是非理解できるまでじっくりsvg画像とにらめっこして自身の納得のいくまで考えてみてください。