ASP.NET MVCで画像を画面内に自動縮小表示する方法|vh/vwとmax-heightで縦横フィット

ASP.NET MVCでスクリーンショットや添付画像を表示すると、画像が大きいだけでページ全体が縦横に伸び、スクロールが必須になることがあります。CSSの%指定では高さが思った通りに効かない理由を整理し、ビューポート基準のvh/vwで“画面内に自動で収める”実装を解説します。

目次

結論:max-width / max-height を「ビューポート基準」で指定すると安定する

まず結論から言うと、画像を「画面サイズに合わせて自動で縮小表示(縦横とも)」したい場合は、幅と高さを固定するのではなく、上限(max-)だけを決めるのが最も事故が少ないです。さらに、上限の基準を「親要素の%」ではなく、ブラウザの表示領域(ビューポート)にすると、ページの高さが不自然に伸びたり、余白が残ったりする問題を避けやすくなります。

最小構成としては、次のCSSだけで「大きい画像は画面内に縮小」「小さい画像は等倍のまま」を両立できます。

/* 画像が大きいときだけ、画面内に収まるよう縮小する */
.screen-shot{
  max-width: 100vw;   /* 画面幅を上限にする */
  max-height: 80vh;  /* 画面高の80%を上限にする(ヘッダー分の余裕) */
  width: auto;       /* 具体的な幅は固定しない */
  height: auto;      /* 具体的な高さは固定しない */
  display: block;    /* 余計な隙間(inline要素由来)を避ける */
  margin: 0 auto;    /* 横方向に中央寄せ(任意) */
}

ポイントは「高さを%で決めない」ことです。max-heightを80vhのように設定すると、画像がどれだけ縦に長くても、表示領域に合わせて自動で縮小され、縦スクロールを極力発生させません。

やりたいことおすすめ指定理由
横はみ出しを防ぐmax-width: 100%; または max-width: 100vw;大きい画像だけ縮小し、小さい画像は等倍表示できる
縦はみ出しを防ぐmax-height: 80vh;(画面に応じて調整)親要素の高さに依存せず、ビューポート基準で確実に効く
縦横比を維持するwidth/heightを固定せず、max-だけ指定ブラウザが元の縦横比を保ったまま縮小する

なぜ height: 90% が効かないのか(余白が残る原因)

「幅はJavaScriptで合わせられたが、高さがうまくいかない」「height: 100% を指定すると、ページ全体が大きいままになって余白が残る」という現象は、CSSの仕様としてかなり典型的です。結論は次のとおりです。

  • %指定の高さは、親要素の高さが“確定値(計算できる値)”で定義されていないと基準がなく、期待通りに計算できない
  • HTML文書は通常、高さがauto(中身次第)で決まりやすく、画像の高さがそのままページ全体の高さに影響する
  • その結果、height: 90%; を書いても「親の高さ」が決まらず、縮まず、スクロールや余白が残る

%指定の高さが成立する条件

CSSの%指定は便利ですが、特にheightだけは落とし穴が多いです。%で高さを指定した場合、ブラウザは「親要素の高さの何%か」を計算します。しかし、親要素の高さがauto(コンテンツによって伸び縮み)だと、計算の土台がありません。土台がない状態で「90%」と言われても、ブラウザは具体的な値に落とせないため、結果として期待通りに縮まりません。

たとえば次のような構造は、見た目は“親divがあるから大丈夫そう”に見えますが、親divの高さがautoなら、子imgの%高さは安定しません。

<div class="container">
  <img class="screen-shot" src="..." />
</div>
.container{
  /* 高さを決めていない(autoのまま) */
}
.screen-shot{
  height: 90%;
}

この場合、.containerは子要素(img)の高さで伸びます。imgは親の90%…のはずが、その親が子で伸びる…という循環関係になり、思ったような縮小が成立しません。ここが「ページ全体の高さが縮まらない」「余白が残る」体感につながります。

html/bodyにheight:100%を付けても解決しないことがある理由

よくある対処として、html, body { height: 100%; }を入れる方法があります。これは「viewportと同じ高さを基準にしたい」という意図では正しいのですが、ページ内に他の要素(ヘッダー、パンくず、説明文、ボタン、フッターなど)があると、結局bodyはコンテンツに引っ張られて伸びます。さらに、min-heightや余白、固定配置(fixed)要素との兼ね合いで、思わぬスクロールが残ることもあります。

つまり、画像だけを“画面内に収めたい”なら、文書全体の高さを%で制御しようとするより、画像側に「画面基準の上限」を与えるほうが実務では安定します。

vh / vw とは何か:%と何が違うのか

vhとvwは、viewport(表示領域)のサイズに対する割合で指定できる単位です。たとえば80vhは「表示領域の高さの80%」です。親要素の高さがautoでも関係なく、常に画面基準で計算されるため、画像の縦方向制御に強いのが特徴です。

単位基準画像縮小での使いどころ注意点
%親要素(containing block)親の高さ/幅が確定しているときに有効親の高さがautoだとheight%は破綻しやすい
vwビューポートの幅横方向を画面幅以内に抑えるスクロールバー分を含み、横スクロールが出ることがある
vhビューポートの高さ縦方向を画面高以内に抑える(今回の本命)モバイルで表示領域が変動する環境がある
px絶対値固定レイアウト(社内ツール等)では手堅い画面サイズ差で崩れやすい

実務で使いやすいCSSテンプレート

現場では「画像だけ置いて終わり」ではなく、説明テキストやボタン(閉じる、ダウンロード、次へ/前へ)を併設することが多いです。その場合、100vhぴったりだとUIが詰まりやすいので、まずは80vh〜90vhあたりを上限にするのがおすすめです。

テンプレートA:ページ上で素直に表示する(最もシンプル)

画像を通常のページ内に表示しつつ、はみ出す時だけ縮小させたい場合の基本形です。

.screen-shot{
  max-width: 100%;
  max-height: 80vh;
  height: auto;
  width: auto;
  display: block;
}

max-width: 100%にしておくと、画像は「親の幅」以内に収まるため、左右スクロールが出にくいです。レイアウトの都合で親が画面幅いっぱい(レスポンシブ)なら、結果として画面幅にもフィットします。

テンプレートB:専用の表示枠に“必ず”収める(ビューア/モーダル向け)

画像を中央に配置し、上下左右に余白を残して“ビューアっぽく”見せたい場合は、画像の親要素(枠)の高さをビューポート基準で確定させ、その枠に対して画像をフィットさせます。ここでobject-fit: contain;が効いてきます。

.image-viewer{
  height: 80vh;                 /* 枠の高さを確定させる */
  width: 100%;
  display: flex;
  align-items: center;          /* 縦中央 */
  justify-content: center;      /* 横中央 */
  overflow: hidden;             /* 枠からはみ出した分は見せない */
}

.image-viewer img{
  width: 100%;
  height: 100%;
  object-fit: contain;          /* 枠の中に収めつつ縦横比を維持 */
}

テンプレートAとの違いは「画像自身に高さを持たせる」点です。Aは“画像の自然サイズを尊重しつつ上限で縛る”方式、Bは“枠に合わせて画像を描画する”方式です。スクリーンショット閲覧や証跡確認の画面など、UXを統一したいときはBが扱いやすいです。

方式向いている場面メリット注意点
上限だけ指定(テンプレートA)記事・詳細画面・一覧の拡大表示CSSが簡単。画像が小さいときは等倍表示中央寄せや背景演出は別途必要
枠にフィット(テンプレートB)画像ビューア、モーダル、検証用ツール見た目が揃う。中央寄せや操作UIを置きやすい枠の高さをvhなどで“確定”させる必要がある

ASP.NET MVC(Razor)での具体例

ASP.NET MVCでは、画像の表示自体はHTMLの<img>なので、解決策の中心はCSSです。Razor側は「クラスを付ける」「必要ならキャッシュ対策や遅延読み込みを入れる」程度で十分です。

View(.cshtml)側

<div class="image-viewer">
  <img
    src="@Url.Content(Model.ImageUrl)"
    class="screen-shot"
    alt="スクリーンショット"
    loading="lazy"
    decoding="async" />
</div>

loading="lazy"やdecoding="async"は必須ではありませんが、画像が重い場合に体感が良くなることがあります。管理画面や社内ツールのように「とにかく素早く開きたい」用途で効きます。

CSSの配置場所(Bundle / wwwroot / Layout)

ASP.NET MVC(.NET Framework)ならBundleでCSSをまとめる運用が多いですし、ASP.NET Core MVCならwwwroot配下に置くことが多いです。重要なのは、画像表示用の共通クラスとして一箇所に集約することです。画面ごとにインラインstyleでmax-height:80vhを書き散らすと、後で調整が必要になったときに漏れが出ます。

ヘッダーやボタンがある画面は calc() で“引き算”する

「80vhでもまだ少しはみ出す」「固定ヘッダーの下に潜り込む」という場合、ヘッダーやフッターの高さ分を引くのが実務的です。ここで役に立つのがcalc()です。

:root{
  --app-header: 64px;  /* ヘッダーの高さ(環境に合わせて) */
  --app-footer: 56px;  /* 下部ボタンの高さ(環境に合わせて) */
  --viewer-gap: 24px;  /* 余白(上下のマージン相当) */
}

.screen-shot{
  max-width: 100%;
  max-height: calc(100vh - var(--app-header) - var(--app-footer) - var(--viewer-gap));
  height: auto;
  width: auto;
  display: block;
}

固定値はプロジェクトによりけりですが、「ヘッダーや操作ボタンがあるのに100vh基準で画像を最大化してしまい、結局スクロールが出る」というのはよくある失敗です。最初から引き算で“表示に使える高さ”を定義すると、画面ごとの差分が吸収しやすくなります。

Bootstrapのモーダルで画像を表示する場合のコツ

スクリーンショット確認はモーダルで開くことも多いです。モーダルは「タイトル」「閉じるボタン」「余白(padding)」があるため、画像にそのまま80vhを与えるより、モーダルの中身(modal-body)の高さを先に制御すると綺麗に収まります。

/* モーダルの中身が画面からはみ出さないようにする */
.modal-body{
  max-height: calc(100vh - 160px); /* ヘッダー/フッター/余白分を見込む */
  overflow: auto;                  /* 必要ならモーダル内でスクロール */
}

/* 画像はmodal-body内に収める */
.modal-body .screen-shot{
  max-width: 100%;
  max-height: 100%;
  height: auto;
  width: auto;
  display: block;
  margin: 0 auto;
}

「絶対にスクロールを出したくない」場合はoverflow: hiddenにし、画像のmax-heightをさらに小さめにする(例:calc(100vh - 200px))と調整しやすいです。一方、画像が極端に縦長(長いログや縦スクロールのスクショ)で、縮小すると読めなくなる場合は、モーダル内スクロールを許可した方がUXが良いケースもあります。目的が「全体の把握」なのか「文字の判読」なのかで、正解が変わります。

モバイルでvhがズレるときの対策(iOS Safariなど)

デスクトップ中心の社内システムなら問題になりにくいのですが、スマホ対応をする場合、100vhが「アドレスバー等を含んだ高さ」で計算される挙動があり、スクロールやチラつきが出ることがあります。対策は大きく2つです。

可能なら新しいビューポート単位(dvh/svh/lvh)を検討する

最近のブラウザでは、アドレスバーの出入りによる表示領域変化を考慮した単位(例:dvh)が使えることがあります。使える環境なら、次のように書くと「実際の表示領域に追従」しやすくなります。

.screen-shot{
  max-width: 100%;
  max-height: 80dvh;  /* 対応ブラウザでは動的に追従 */
}

互換性重視なら、JavaScriptで–vh変数を作る

より確実にしたい場合は、window.innerHeightを使ってCSS変数--vhを作り、vhの代替として使う方法があります。

(function () {
  function setVh() {
    var vh = window.innerHeight * 0.01;
    document.documentElement.style.setProperty('--vh', vh + 'px');
  }
  window.addEventListener('resize', setVh);
  setVh();
})();
.screen-shot{
  max-width: 100%;
  max-height: calc(var(--vh, 1vh) * 80); /* --vhが無い環境でも1vhでフォールバック */
}

JavaScriptを足すのは「CSSだけで完結したい」方針と相反しますが、スマホでの安定性を最優先する場合に有効です。ASP.NET MVCの画面でも、そのまま共通JSとして差し込めます。

よくある落とし穴とトラブルシューティング

vh/vwを使っても「なぜか横スクロールが出る」「1pxだけはみ出す」「画像の下に謎の余白が出る」といった微妙なズレが残ることがあります。原因は画像そのものではなく、周辺のCSS(padding、border、display、スクロールバー幅)にあることが多いです。

症状よくある原因対処
横に1px〜数pxのスクロールが出るmax-width: 100vwがスクロールバー幅まで含む/親要素のpaddingまずはmax-width: 100%に切り替える。必要なら親にbox-sizing: border-box;
画像の下に細い隙間が出るimgがinline要素扱いで、行ボックスのベースラインが残るimg{ display:block; }を付ける
縮小はするが上下に余白が大きい安全側にmax-heightを小さくしすぎ/親の上下padding80vh→90vhなどに調整。親のpaddingも見直す
モーダル内で画像が切れるモーダルの高さ制限がない/overflowの指定が不適切.modal-body{ max-height: ...; overflow:auto; }を先に決める
高さ%が効かずページが伸びる親要素のheightがautoで確定していないvhに切り替える/枠をvhで確定し、object-fitでフィット

「モニターサイズに合わせる」の考え方:物理サイズではなくビューポート

要件として「モニターサイズに合わせて縮小したい」という表現が出てくることがありますが、Web開発で扱うのは通常、物理的なモニターのインチや解像度ではなく、ブラウザの表示領域(ビューポート)です。同じモニターでも、ウィンドウサイズやズーム率、開発者ツールの表示でビューポートは変わります。だからこそ、vh/vwで「今見えている領域」を基準にするのが自然です。

実運用で差がつく:画像の品質・読み込み・拡大表示の設計

画像を画面内に縮小できても、運用面で困るケースがあります。代表例は「縮小しすぎて文字が読めない」「巨大画像で読み込みが遅い」です。画像表示はUI/UXとパフォーマンスが直結するので、次の観点も押さえておくと完成度が上がります。

  • 証跡用途(スクショ):全体が見える縮小表示+クリックで別タブ原寸(またはズームUI)を用意すると、確認が速い
  • 写真用途:縮小表示でもある程度見栄えが出るが、サムネイル生成や圧縮(サーバー側)を検討すると表示が軽くなる
  • 回線が弱い環境:loading="lazy"、適切な画像形式(WebP等)、キャッシュ制御で体感が変わる
  • アクセシビリティ:altは空にせず、意味のある代替テキストを入れる(社内ツールでも後で効く)

よくある質問

max-heightは80vh固定でいい?

多くの画面では80vh〜90vhで問題ありませんが、固定ヘッダーや下部ボタンがある場合はcalc(100vh - ...)で引き算した方が確実です。逆に、画像だけを単独で見せる専用ページなら90vh〜95vhまで広げてもよいでしょう。最終的には「画像の可読性」と「操作UIの置き場」のバランスで決めるのがおすすめです。

width: 100vw と max-width: 100% はどちらを使うべき?

親コンテナがレスポンシブに画面幅へ追従しているなら、まずはmax-width: 100%が安全です。100vwはビューポートを基準にできて直感的ですが、縦スクロールバーの幅まで含む関係で、環境によっては横スクロールが出ることがあります。横スクロールが出て困る場合は、100%に戻すか、親要素の設計(paddingやoverflow)を見直すのが近道です。

object-fit: contain; は必須?

テンプレートAのように「max-width / max-heightで上限だけを縛る」方式なら必須ではありません。object-fitは、画像にwidthとheightを与えて枠に“はめ込む”ときに効果が出ます。ビューア枠に対してwidth:100%; height:100%;でフィットさせたい場合にセットで使うと理解しやすいです。

まとめ:%の高さにこだわらず、ビューポート基準で上限を決める

画像が画面より大きいときに縦横とも自動で縮小したいなら、高さ%が効かない理由(親の高さが確定していない)を押さえたうえで、vh/vwで「画面基準の上限」を与えるのが最短ルートです。まずはmax-height: 80vh;とmax-widthの組み合わせを共通クラスとして用意し、モーダルや固定ヘッダーのある画面ではcalc()で引き算して微調整する——この流れにしておくと、ASP.NET MVCのどの画面でも再利用しやすく、スクロールや余白の悩みが大幅に減ります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次