PAYATHON 2026

为什么 Firefox 在 scale 过小时无法渲染 outline?

排障赵师傅

缩小元素时,它的 outline 也会一起缩小。原本 1px 的轮廓经过 scale(0.48) 后,几何宽度只有 0.48px。Firefox 在处理这类细线的像素对齐和抗锯齿时,容易出现整条边消失或四边粗细不一的情况。

要让缩放后的轮廓仍然完整、宽度保持在约 1px,可以按缩放比例的倒数补偿 outline-width。如果只需要改变矩形大小,直接设置最终的 width、height 更稳妥。

为什么会消失,或者只显示两条边?

按照 CSS Transforms 的渲染模型,transform 在元素完成尺寸计算和定位后改变其视觉呈现。因此,outline: 1px solid black 中的 1px 也会随元素一起缩放。

当缩放比例为正数,且两个方向等比例缩放时,轮廓的几何宽度满足:

缩放后的线宽 = 原始线宽 × scale

题目中的三个比例对应的线宽如下:

scale缩放后的几何线宽
0.480.48px
0.500.50px
0.520.52px

这些宽度的单位都是 CSS 像素。实际绘制时,浏览器还要将它们映射到设备像素,并处理像素对齐和抗锯齿。Mozilla 的相关缺陷记录确认,细边框在缩小时可能消失,四条边也可能因为落在不同的亚像素位置而显示出不同粗细。

题目中 scale(0.5) 时只剩右边和下边,就可以用这一点解释:四条边的几何宽度相同,但映射到像素网格后的覆盖情况可能不同。仅凭示例,无法确定所用 Firefox 版本内部每一步如何取整。

0.5 也不是 Firefox 统一规定的临界值。元素位置、设备像素比、页面缩放和浏览器版本都会影响结果。Mozilla 的另一条复现记录显示,调整页面缩放会改变缺边现象。

换成 border 或 box-shadow,细线仍会参与缩放,也可能遇到类似问题。如果基础尺寸较小,且始终使用大于 1 的缩放比例,1px 线条会被放大,更容易保持可见。

保留 scale:按比例补偿轮廓宽度

将缩放比例存入 CSS 自定义属性,同时用于 transform 和 outline-width:

.tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100px;
}

.rect {
  --scale: 1;

  width: 100px;
  height: 100px;
  background: #D3D3D3;

  transform: scale(var(--scale));
  outline: calc(1px / var(--scale)) solid black;
}

.label {
  font-size: 0.7rem;
  font-family: monospace;
}
<div class="tile">
  <div class="rect" style="--scale: 0.48"></div>
  <span class="label">0.48</span>
</div>

<div class="tile">
  <div class="rect" style="--scale: 0.5"></div>
  <span class="label">0.50</span>
</div>

<div class="tile">
  <div class="rect" style="--scale: 0.52"></div>
  <span class="label">0.52</span>
</div>

原来行内的 transform: scale(...) 要替换为 --scale,避免两处使用的比例不一致。

以 0.48 为例,缩放前的轮廓宽度约为 2.0833px,缩放后的几何宽度回到 1px:

(1px / 0.48) × 0.48 = 1px

这样可以在元素继续缩小的同时,让轮廓保持约 1 CSS px。实际绘制仍可能受线宽取整和像素对齐影响,需要在目标环境中确认效果。

将轮廓宽度固定为 2px 只适用于有限的缩放范围,继续缩小时,线条仍会变得很细。改成 1.1px 也无法保证任意比例下都能完整显示。

只改变矩形大小:直接设置最终尺寸

示例中的矩形只有背景和轮廓,没有必须整体缩放的内容,可以直接把尺寸设为 48px、50px、52px,让轮廓保持 1px,避免被 scale 压缩为亚像素线宽:

.rect {
  width: var(--size);
  height: var(--size);
  background: #D3D3D3;
  outline: 1px solid black;
  transform: none;
}
<div class="rect" style="--size: 48px"></div>
<div class="rect" style="--size: 50px"></div>
<div class="rect" style="--size: 52px"></div>

两种方案对布局的影响不同:原来的 100px 元素经过 transform 后,仍按原始尺寸参与普通布局;直接修改宽高会改变布局占用,标签位置和元素间距可能需要随之调整。

使用时需要注意什么?

  • 倒数补偿示例适用于 --scale > 0 的等比例缩放,不能除以 0。非等比例缩放无法用一个 outline-width 同时补偿两个方向。
  • 如果祖先元素也有缩放,需要考虑累计比例。仅补偿当前元素的 scale,无法抵消祖先的缩放。
  • 1 CSS px 不一定等于一个设备像素,页面缩放也会影响两者的关系,见 MDN 对 devicePixelRatio 的说明。
  • 如果补偿线宽后仍然缺边,应检查祖先的 overflow: hidden、overflow: clip 等裁剪设置,并在实际使用的 Firefox 版本和页面缩放比例下验证。

备注:内容仅供参考。