PerfectVector
著者: Irene Kim2 分で読める

SVGのpreserveAspectRatio:形を伸ばさずに配置する

SVGの配置にmeet、slice、noneを使い分けます。同じ図形で整列、クリッピング、余白を確認し、SVGの座標変換とCSSのobject-fitを区別します。

目次

SVGのviewBox全体を、形を伸ばさず表示領域内に収めたい場合はpreserveAspectRatio="xMidYMid meet"を使います。表示領域を覆い、内容の一部が領域外にはみ出してもよい場合はsliceを使います。水平方向と垂直方向を異なる倍率で拡大縮小する意図がある場合だけnoneを使ってください。

重要なのは、viewBoxで定義する画像の座標矩形と、画像を表示するビューポートを区別することです。フィッティングは、一方をもう一方へどう対応させるかを変えます。ロゴを描き直したり、元のキャンバスの余白を取り除いたり、切り抜かれたパスを永久に削除したりする操作ではありません。

まず収め方を選ぶ

目的属性値縦横比が異なる場合の注意点
viewBox全体を表示するxMidYMid meetいずれかの軸方向に余白が残ります
ビューポートを覆うxMidYMid sliceviewBoxの一部がビューポート外にはみ出します
各軸を独立した倍率で満たすnone円などの比率が変形することがあります

MDNのpreserveAspectRatioリファレンスでは、これらの値と既定値xMidYMid meetを説明しています。svg要素でこの対応付けを行うにはviewBoxが必要です。属性名は大文字と小文字を区別するため、AとRを大文字のままにします。

ロゴでは、まずmeetを使います。通常は、文字の一部が欠けたり円が楕円になったりするよりも、余白が残るほうが適切です。装飾用の背景なら、意図的な切り抜きを許容できる場合があります。整列方法を選ぶ前に、その判断をしてください。

枠内に収めた完全なエンブレム、拡大して切り抜いたエンブレム、引き伸ばしたエンブレムの概念比較
構図全体を保持する、切り抜きながら枠を覆う、変形を許容するという3つの配置方法の図解です。以下のブラウザーでの測定には、別の独自SVG素材を使用しています。

2種類のビューポートで同じエンブレムを試す

この独自の例は、円、矩形、中央からずれた三角形で構成しています。円によって引き伸ばしが見えやすくなり、不均等な構成要素によって、対称なバッジよりも整列の変化を見つけやすくなります。

次の内容をHTMLファイルに貼り付け、ブラウザーで開いてください。

<svg width="300" height="100" viewBox="0 0 200 100"
     preserveAspectRatio="xMidYMid meet"
     xmlns="http://www.w3.org/2000/svg"
     role="img" aria-label="Circle, rectangle, and triangle emblem"
     style="background:#f4ead4; overflow:hidden">
  <circle cx="40" cy="50" r="25" fill="#147d78" />
  <path d="M85 20 H130 V80 H85Z" fill="#e5a340" />
  <path d="M145 70 L170 25 L190 70Z" fill="#183549" />
</svg>

viewBoxの幅は200単位、高さは100単位で、縦横比は2:1です。最初のビューポートは300 × 100 CSSピクセルで、縦横比は3:1です。属性だけを変えてxMidYMid sliceとnoneを比較します。次にビューポートをwidth="100" height="300"に変更して、同じ比較を繰り返します。

小さなセレクターベースのテスト用ツールを使い、Chromeでこの形状を描画しました。表示値には、画像グループの現在の変換行列を使用しました。得られた倍率と移動量は次の値に一致しました。sliceの約−25ピクセルのずれは、ここでは丸めています。

ビューポート設定水平/垂直の倍率中央配置による移動量(ビューポートのピクセル)
300 × 100meet1 / 1水平50、垂直0
300 × 100slice1.5 / 1.5水平0、垂直−25
300 × 100none1.5 / 10 / 0
100 × 300meet0.5 / 0.5水平0、垂直125
100 × 300slice3 / 3水平−250、垂直0
100 × 300none0.5 / 30 / 0

縦長のビューポートでは、meetで小さな3つの図形がすべて見えました。中央配置のsliceでは拡大した構図の一部だけが見え、金色の矩形が大きく占めました。noneでは円が細長くなりました。これはChromeでこの素材を確認した結果であり、すべてのグラフィックエディターで同じ読み込み動作を保証するものではありません。

原点がゼロのこの例では、倍率の規則は単純です。ビューポートの幅をviewBoxの幅で割り、高さも同様に割ります。meetは両軸に小さいほうの比率を使い、sliceは大きいほうを使います。noneは2つの比率を別々に使います。SVGの座標系仕様では、整列やゼロ以外のviewBox原点も含めた対応付けを定義しています。

整列で余白の位置を決める

最初のキーワードは整列を制御します。xMin、xMid、xMaxは、viewBoxの左端、水平中央、右端をそろえます。YMin、YMid、YMaxは垂直方向の同じ操作です。基準はviewBoxの矩形であり、実際のパスの周囲に余白を含んでいる場合があります。

縦長のテスト用ビューポートでは、次のようになります。

  • xMinYMin meetは、収めたviewBoxを垂直中央ではなく上部に配置します。
  • xMinYMin sliceは、viewBoxの左側をビューポートの左側にそろえます。拡大した右側はビューポート外にはみ出します。
  • xMidYMid sliceは、水平方向のはみ出しを左右に分配します。

テストツールの中央配置をxMinYMinに変えました。meetとsliceの移動量はどちらもゼロになりましたが、倍率はそれぞれ0.5と3のままでした。整列で構図は移動しても、収めるための倍率は変わりません。

整列の変化が見えるのは、余白やはみ出しがある軸だけです。横長のmeetでは、viewBoxがすでに高さを満たしているため、YMidをYMinに変えても垂直方向に移動する余地がありません。

クリッピングを明示的に選ぶ

sliceは倍率を選びます。ビューポート外にはみ出した部分が見えるかどうかは、クリッピングで決まります。この例では、切り抜きが予測できるようoverflow:hiddenを明示しています。

縦長のテストでoverflowをvisibleにすると、拡大した円と三角形がsliceのビューポート外に現れました。形状が消えたわけではありません。MDNのSVG overflowリファレンスでは、SVGの使用状況による既定スタイルの違いも含め、overflowとビューポートのクリッピングの関係を説明しています。

sliceをパスの切り取り操作として扱わないでください。納品ファイルに残した形状だけを含める必要がある場合は、実際の編集操作を行って確認します。矩形以外の表示窓を作りたい場合には、clipPathが適切なことがあります。

同様に、meetが保証するのはviewBoxが収まることです。描画内容全体が収まるとは限りません。ストローク、フィルター、元の矩形外のパスは、その外側にはみ出す可能性があります。属性だけを信頼せず、見える端を確認してください。

整列を調整する前に元データの余白を直す

小さなマークが、とても大きなviewBoxの中央にだけ存在する場合を考えてみてください。meetは空の余白を含む座標矩形全体を収めます。そのため、倍率が正しくても結果が小さすぎるように見えます。

エディターで書き出し用のキャンバスやアートボードを確認してください。構図を詰めるためにviewBoxを変更する場合は、必要なストロークや効果を含め、各配置を再確認します。すべての余白を自動で取り除かないでください。ブランドマークの周囲に意図的なクリアスペースがある場合もあります。

実寸の問題は別です。SVGが誤ったミリメートル寸法で読み込まれる場合は、SVGのサイズ調整手順が必要です。配置の整列を変えても、単位の解釈は通常直りません。

インラインSVGと画像のフィッティングを区別する

インラインSVGでは、対象のsvg要素の属性を変更します。CSSは要素のボックスのサイズを決め、preserveAspectRatioはviewBoxをそのボックスに対応させます。

外部SVGをHTMLの画像要素で使う場合は、CSSで置換画像の収め方を制御します。

<img class="brand-mark" src="emblem.svg" alt="Brand emblem">
<style>
  .brand-mark {
    width: 300px;
    height: 100px;
    object-fit: contain;
    object-position: center;
  }
</style>

MDNのobject-fitドキュメントでは、置換コンテンツがCSSボックスにどう収まるかを説明しています。これは、SVG文書内の対応付けを編集することとは別の層です。object-fit: containでも、元データにある空白のキャンバスは取り除けません。組み込み方を決める際は、インラインSVGと画像要素の比較を参照してください。

SVGには、HTMLのimgとは異なり、単語を省略せずに書くimage要素もあります。そのpreserveAspectRatioは、参照画像をその要素に設定した矩形内へどう収めるかを制御します。MDNの属性リファレンスが説明するように、同じ要素にviewBoxを指定するという一般的な要件の例外です。この3つを区別すれば、HTML画像にSVG専用の属性を追加して直そうとする混乱を防げます。

配置する前に元の比率を保持する

独自のマークがPNGやJPGしかなく、編集可能なベクター形状が必要な場合は、レイアウトを作る前にPerfectVectorでSVGの元データを復元できます。まず輪郭、小さな開口部、キャンバスの境界を確認してください。配置処理は現在の形状を維持しますが、歪んだ元データを修復したり、復元されなかった細部を作り直したりはできません。

きれいなSVGがすでにあれば、そのままフィッティングの確認へ進めます。上の単純なエンブレムなら、手書きの形状で十分です。画像のベクター化の概要では、ラスター画像からパスを復元することが役立つ場面を説明しています。

ラスターしかないマークでは、PerfectVectorで元画像を試し、SVGを確認してから、切り抜きを検討する前にmeetで横長と縦長の配置を比較してください。元のベクターマスターを保持すれば、レイアウトの判断を後で戻せます。

よくある質問

meetを使ってもSVGに余白が残るのはなぜですか? ビューポートとviewBoxの縦横比が異なると、等倍率で拡大縮小した後に余白が残ります。また、元のviewBoxがパスの周囲に余白を含んでいる場合もあります。整列はその矩形を移動する操作で、内部の余白は取り除きません。

sliceは画像を永久に切り抜きますか? いいえ。ビューポートを覆うための倍率を変えます。見える範囲はoverflowとクリッピングで制御します。別途編集しない限り、元のパス形状は文書内に残ります。

noneで円が楕円になるのはなぜですか? 水平と垂直に異なる倍率を許容するためです。ビューポートとviewBoxの縦横比が違うと、円が楕円になることがあります。等倍率の拡大縮小を維持するには、整列キーワードとともにmeetまたはsliceを使います。

参考資料

  1. MDN preserveAspectRatio — フィッティング値、整列、既定値、SVG imageの例外を定義しています。
  2. SVGの座標系仕様 — ビューポートの倍率と移動量の計算を定義しています。
  3. MDN SVG overflow — overflowによるビューポートのクリッピング制御を説明しています。
  4. MDN object-fit — CSSボックス内に置換画像を収める方法を説明しています。

ブログのその他の記事

編集しやすい
よりきれいなSVGから始めましょう