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を小さくしすぎ/親の上下padding | 80vh→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のどの画面でも再利用しやすく、スクロールや余白の悩みが大幅に減ります。

コメント