写了 aspect-ratio 却不是那个比例:自动尺寸、最小高度与图片固有比例
写了aspect-ratio:16/9,元素却不是16:9,先检查是否又写了固定宽高。aspect-ratio提供首选比例,参与自动尺寸计算,并不是凌驾于宽高和最小尺寸之上的强制裁决。
卡片封面、视频占位和响应式图表都适合比例布局,但它们要回答的问题不同:是让盒子按比例取得高度,还是让图片内容按比例放进盒子?前者是aspect-ratio的职责,后者通常还需要object-fit。把两者混为一谈,容易修好图片裁切,却让卡片尺寸仍然不符。
这次用三个普通盒子与一张已有固有比例的图片,对比同一个宽度变化怎样产生不同高度。没有脚本计算并回写高度,尺寸结果来自CSS自身。
在四种约束下观察同一个属性
先保存source.svg,图片固有尺寸640×320,比例是2:1。
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="320" viewBox="0 0 640 320">
<rect width="640" height="320" fill="#376c9f"/>
<text x="190" y="180" fill="white" font-size="58" font-family="sans-serif">2 : 1</text>
</svg>
在同目录保存ratio-demo.html并打开:
<!doctype html>
<html lang="zh-CN">
<meta charset="utf-8">
<title>首选比例与尺寸约束</title>
<style>
body { margin: 24px; font: 16px sans-serif; }
.samples { --width: 320px; display: flex; align-items: flex-start; flex-wrap: wrap; gap: 24px; }
.samples.narrow { --width: 200px; }
.sample { width: var(--width); }
.box { width: 100%; aspect-ratio: 16 / 9; background: #dceafa; outline: 1px solid #345; }
.both { height: 120px; }
.minimum { min-height: 220px; }
img { display: block; width: 100%; height: auto; aspect-ratio: auto 16 / 9; }
pre { white-space: pre-wrap; }
</style>
<button id="toggle" type="button">切换 320 / 200px 宽度</button>
<div class="samples">
<div class="sample"><p>自动高度</p><div id="auto" class="box"></div></div>
<div class="sample"><p>同时给定高度 120px</p><div id="both" class="box both"></div></div>
<div class="sample"><p>最小高度 220px</p><div id="minimum" class="box minimum"></div></div>
<div class="sample"><p>图片 auto 16/9,已加载源比例 2:1</p><img id="image" src="source.svg" alt="固有二比一示意图"></div>
</div>
<pre id="report" role="status"></pre>
<script>
function report() {
document.querySelector('#report').textContent = ['auto', 'both', 'minimum', 'image'].map(id => {
const element = document.getElementById(id);
const box = element.getBoundingClientRect();
return `${id}: ${box.width}×${box.height}; CSS比例=${getComputedStyle(element).aspectRatio}`;
}).join('\n');
}
document.querySelector('#toggle').addEventListener('click', () => {
document.querySelector('.samples').classList.toggle('narrow');
report();
});
document.querySelector('#image').addEventListener('load', report);
window.addEventListener('resize', report);
report();
</script>
</html>
320px宽时,普通自动高度盒子为180px;显式高度那项仍是120px;最小高度那项至少220px;图片加载后采用2:1固有比例,得到160px高。切到200px宽,第一项为112.5px,固定高度与最小高度分别仍受120和220限制,图片为100px高。
这四项都能在开发者工具看到aspect-ratio声明,却不应该拥有同一个最终比例。属性存在只证明参与过相应计算,不证明它战胜了其他约束。
先找到哪一维允许自动计算
普通盒子的第一项给宽度、不指定高度,比例可以用来推导另一维。第二项宽高都明确,高度不再需要由比例推导;此时继续提高选择器优先级,通常无法解决根本问题,因为并非属性没有应用。
真实组件常在不同样式层同时写宽高:基础卡片定义比例,业务页面又给height,媒体查询再改宽度。排查应看最终计算样式,而不是只找到最先出现的那条aspect-ratio就结束。
如果设计要求无论内容多少都维持封面比例,应让内容适配那份盒子,并处理溢出。若卡片需要随文字自然长高,就不应再把整个卡片都锁进封面比例。把比例只放在媒体区域,通常更符合两类内容各自的尺寸需要。
最小高度一项则说明比例是首选值,尺寸约束仍然成立。设计中同时写最小触控区域、固定最小高度和任意窄宽度时,三者可能不能一起维持16:9;应明确哪个条件优先,而不是把偏离比例一概视为浏览器错误。
auto 与比例并列时,图片还有固有比例
auto 16 / 9对有固有比例的替换元素,不等于永远使用16:9。图片尚未取得固有比例时,可使用所给比例参与布局;取得固有比例后,auto允许采用资源自身比例。本文加载后的源图明确是2:1,所以结果是320×160。
如果产品要求封面无论原图比例如何都保持16:9,应选择明确的盒子比例,并用合适object-fit处理内容。不能一边要求保留图片固有比例,一边又要求所有盒子高度相同,然后把矛盾归给auto关键字。
为了减少加载时跳动,资源比例信息应尽早与真实内容一致。错误占位比例在加载前后产生高度变化,不是网络速度本身能修复的。给HTML尺寸或CSS占位值时,要确认它们描述源图还是展示盒子,再观察最终替换过程。
本地SVG加载很快,本文没有把截图里的某一帧冒充网络加载过程。如果要验证慢图占位,可以在浏览器网络工具中控制请求,再分别记录加载前后尺寸;需要区分尚无固有比例与最终资源比例两个状态。
比例值本身描述宽与高的比值,16/9与32/18表达相同关系,不代表固定十六像素宽、九像素高。它也不是一份最大尺寸约束;宽度由外部布局增大时,自动高度会按相应比例增大。需要限制封面最大高度,还要写出那份尺寸边界,并接受它可能使最终比例变化。
用脚本监听resize再计算高度,通常会复制浏览器已经能完成的工作,还可能在布局变化和脚本回写之间造成反馈。只有业务确实需要额外测量时才引入这层流程;基础比例可以由CSS表达,就先让尺寸来源保持单一,排错也更容易确定是哪份约束在起作用。
盒模型会改变你测到的比例
getBoundingClientRect报告元素边框盒尺寸。本页用outline画框,不加入真实边框或padding,因此测量容易对应当前CSS宽高。若在盒子里增加边框或内边距,还要结合box-sizing判断比例计算所采用的盒子与最终外部尺寸。
例如内容区按16:9取得尺寸以后,再向两边加等量padding,外部边框盒就未必仍是16:9。不要只用截图外框的宽高除一下,就断言比例计算无效。应先写清设计要求究竟约束内容区还是卡片外框。
元素还可能受到Flex/Grid的拉伸、最小尺寸和可用空间约束。本文把样本放在明确宽度包装里,并关闭纵向拉伸,让核心差异不被其他布局条件掩盖。复制到真实页面时,要把这些条件逐项放回,而不是假定属性在所有父布局里都独立工作。
最后删掉both那项height:120px,再切换宽度,它应回到自动比例;删掉minimum的min-height后也应如此。两次局部禁用能直接证明,偏离比例来自已写下的尺寸合同,而不是少加了某个浏览器前缀。