WebGL Tutorial
and more

按比率最大化缩放子容器

撰写时间:2025-03-12

修订时间:2025-04-08

最大化缩放子容器边框

使用aspect-ratio配合max-widthmax-height可让子容器的边框按比例最大化地顶住父容器。

#parent { width: 100vw; height: 100vh; background: hsla(120, 50%, 50%, 0.2); display: flex; } #child { aspect-ratio: 4 / 2; max-width: 100%; max-height: 100%; margin: 0 auto; background: #222; } p { color: #ccc; text-align: center; }

Some texts here.

这种方式,只能改变子容器的宽度与高度值,但不会缩放其内容:无论子容器的边框如何变化,其内部文本大小不会改变。

scale

我们现在的目标是,在子容器的边框顶住父容器边框的同时,让子容器内的内容也随之缩放。较为典型的应用场景为,将一张较大的图像最大化地按比率缩放进父容器中。

这里使用CSStransform技术来解决此问题。

先解决布局的问题。

body { height: 100vh; } div { width: 100px; height: 300px; transform: scale(0.5); transform-origin: 0 0; box-sizing: border-box; border: 1px solid green; background-color: #555; }

Some texts here.

默认情况下,子标签的高度值缩小后,body的高度值不会自动缩小。

bodyheight属性值设置为100vh,可在子标签应用缩放后,其尺寸自动调整回到整个客户端区域内。

上面代码的效果,子容器的高度值超出了客户端高度值,手工应用缩放后,整个子容器正好顶住父容器,同时不破坏父容器原来的尺寸大小。

下节解决如何自动计算缩放因子的问题。

calc

CSScalc函数虽然许多时候很有用,但在涉及本例的应用中,却主要存在着以下限制:

  1. calc函数的除数不能带有单位
  2. 标签的widthheight等属性值必须带有单位
  3. scale函数的参数不能带有单位

先解决第1及第2个问题。

body { height: 100vh; } :root { --w-value: calc(100 * 3); --h-value: calc(100 * 2); --scale-ratio: calc(var(--h-value) / var(--w-value)); --div-width: calc(var(--w-value) * 1px); --div-height: calc(var(--h-value) * 1px); } div { width: var(--div-width); height: var(--div-height); transform: scale(var(--scale-ratio)); transform-origin: 0 0; box-sizing: border-box; border: 1px solid green; background-color: #555; }

Some texts here.

不带单位的值用于计算缩放因子,带单位的值用于设置标签的尺寸大小。

第二步,尝试解决解决第3个问题。

设置Y轴上的缩放因子时,客户端高度为150px,则可使用以下代码150 / var(--h-value)

body { height: 100vh; } :root { --w-value: calc(100 * 3); --h-value: calc(100 * 2); --scale-ratio: calc(150 / var(--h-value)); --div-width: calc(var(--w-value) * 1px); --div-height: calc(var(--h-value) * 1px); } div { width: var(--div-width); height: var(--div-height); transform: scale(var(--scale-ratio)); transform-origin: 0 0; box-sizing: border-box; border: 1px solid green; background-color: #555; }

Some texts here.

但若使用100vh / var(--h-value)则不行:scale函数的参数不能带有单位。

Chrome在这里就卡住了。

鉴于上述比较复杂的规则,这里最灵活的方法就是请JavaScript来帮忙了。

body { width: 100vw; height: 100vh; } div { width: calc(100px * 3); height: calc(100px * 2); box-sizing: border-box; border: 1px solid green; background-color: #3F3F3F; }
const body = document.body; const div = document.querySelector('div'); let bodyWidth = getComputedStyle(body).getPropertyValue('width'); let bodyHeight = getComputedStyle(body).getPropertyValue('height'); let divWidth = getComputedStyle(div).getPropertyValue('width'); let divHeight = getComputedStyle(div).getPropertyValue('Height'); let xScale = parseFloat(bodyWidth) / parseFloat(divWidth); let yScale = parseFloat(bodyHeight) / parseFloat(divHeight); let scaleFactor = Math.min(xScale, yScale); div.style.setProperty('transform', `scale(${scaleFactor})`); div.style.setProperty('transform-origin', '0 0');

Some texts here.

先分别计算出X轴与Y轴上的缩放因子,取其最小值,再为div动态添加变换属性。

修改divwidthheight属性值,该标签总能最大化地按比例缩放。

object-fit

object-fit可像SVGviewBox一样,指定是否按比例缩放。

#container { margin: 0.2em; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1em; & div { border: .1px solid gray; border-radius: 0.5em; text-align: center; & img { width: 100px; aspect-ratio: 2 / 1; border: 1px solid gray; filter: brightness(80%); background-color: #555; } & p { color: thistle; margin: 0.2em auto; } } }
let fitTypes = ['fill', 'contain', 'cover', 'none', 'scale-down']; function createImgs() { let target = document.querySelector('#container'); for (let fitType of fitTypes) { let img = document.createElement('img'); img.src = "/js/esm/three/manual/examples/resources/images/happyface.png"; img.style.objectFit = fitType; let p = document.createElement('p'); p.textContent = fitType; let div = document.createElement('div'); div.appendChild(img); div.appendChild(p); target.appendChild(div); } } createImgs();

只有none不缩放,其余值都会缩放。

在缩放的值中:

  • 只有fill不按比例缩放,其只是简单的边角定位。
  • 在按比例缩放的值中:
    • cover取宽高的最大值按比例缩放,超出范围部分被裁剪。
    • contain按比率缩放进盒子中,即其内容将完全显示在盒子中。
    • scale-down
      • 如果图片比盒子小,不缩放,效果等同于none
      • 如果图片比盒子大,则向下按比例缩放,效果等同于contain

object-position

上节的containscale-down在盒子中有留白,可使用object-position来定位X轴及Y轴的偏移值。

img { border: 1px solid gray; filter: brightness(80%); background-color: #555; object-fit: contain; } #horz img { width: 100px; aspect-ratio: 2 / 1; &:nth-of-type(1) {object-position: 0 0;} &:nth-of-type(2) {object-position: 50% 0;} &:nth-of-type(3) {object-position: 100% 0;} } #vert img { height: 100px; aspect-ratio: 1 / 2; &:nth-of-type(1) {object-position: 0 0;} &:nth-of-type(2) {object-position: 0 50%;} &:nth-of-type(3) {object-position: 0 100%;} } #container { & div { margin: 0.5em; padding: 0.5em; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); justify-items: center; border: .1px solid gray; border-radius: 0.5em; & p { color: thistle; margin: 0.1em auto; grid-row: 2; } } }
let fitTypes = ['fill', 'contain', 'cover', 'none', 'scale-down']; function initImgs() { let imgs = document.querySelectorAll('img'); imgs.forEach(img => { img.src = `/js/esm/three/manual/examples/resources/images/happyface.png`; }); } initImgs();

left

center

right

top

middle

bottom

从效果上看,类似于使用background-image设置背景图像,但这种方式可以直接设置img的样式。

参考资源

  1. CSS Typed Object Model API
  2. CSS Images Module Level 3: object-fit