Skip to content
Inspire - Capo Productions

等比缩放适配容器

推导

设:

  • 容器尺寸为 Wc×HcW_c \times H_c(containerWidth, containerHeight)
  • 图片原始尺寸为 Wi×HiW_i \times H_i(imgWidth, imgHeight)
  • 缩放倍数为 s>0s>0。等比缩放后图片尺寸为 sWi×sHisW_i \times sH_i

要求“图片完整放入容器且不裁剪”,也就是:

{sWiWc(宽度不能超出容器)sHiHc(高度不能超出容器)\begin{cases} sW_i \le W_c \quad\text{(宽度不能超出容器)}\\[4pt] sH_i \le H_c \quad\text{(高度不能超出容器)} \end{cases}

从不等式解出 ss 的上界:

sWcWisHcHis \le \frac{W_c}{W_i} \quad\text{且}\quad s \le \frac{H_c}{H_i}

因此 ss 必须同时小于等于这两个数,所以 允许的最大缩放值(也就是我们通常想取的,使图片尽可能大但仍然完整显示)就是这两个上界中的较小者:

s=min ⁣(WcWi,  HcHi)\boxed{s = \min\!\left(\frac{W_c}{W_i},\; \frac{H_c}{H_i}\right)}

直观解释:两个比值分别表示「在只受宽度限制时能放多大」和「在只受高度限制时能放多大」。要同时满足宽和高这两个限制,必须取两者都允许的规模 —— 也就是较小的那个。

留白方向

rw=WcWi,rh=HcHir_w=\frac{W_c}{W_i},\quad r_h=\frac{H_c}{H_i}

  • rw<rhr_w < r_h:取 s=rws=r_w,宽度被“刚好撑满”,高度小于容器 ⇒ 上下留白(垂直居中)
  • rw>rhr_w > r_h:取 s=rhs=r_h,高度被“刚好撑满”,宽度小于容器 ⇒ 左右留白(水平居中)
  • 若相等:恰好铺满,无留白。

额外说明

  • 如果你不允许放大(只缩小),可以把结果限定为 s = min(1, min(r_w, r_h))
  • 如果允许放大(例如把小图放大填满容器),就直接用上面的公式即可(可能 s>1s>1)。

JS 函数

js
function fitImage(containerWidth, containerHeight, imgWidth, imgHeight, allowUpscale = true) {
  const rw = containerWidth / imgWidth
  const rh = containerHeight / imgHeight
  let scale = Math.min(rw, rh)
  if (!allowUpscale)
    scale = Math.min(1, scale)

  let mode
  if (Math.abs(rw - rh) < 1e-9)
    mode = 'none' // 刚好铺满
  else if (rw < rh)
    mode = 'vertical-letterbox' // 上下留白(垂直居中)
  else mode = 'horizontal-letterbox' // 左右留白(水平居中)

  return { scale, mode, renderedWidth: imgWidth * scale, renderedHeight: imgHeight * scale }
}

示例

容器 800×600,图片 1600×1000

rw=0.5, rh=0.6s=0.5r_w=0.5,\ r_h=0.6 \Rightarrow s=0.5

结果:图片宽度刚好 800,高度变为 500 ⇒ 上下留白