海报标题撑满固定横栏,别再用 JS 逐像素逼近了——Chrome 150 的 text-fit 一行搞定
海报标题要刚好撑满那条固定宽度的横栏,你写了三种方案:一种用 JS 量容器再算字号,一种用 SVG 拉伸文字,最后一种干脆放弃、把字号写死。前两种在窗口一 resize 就跑偏,第三种在 i18n 一换语种就露馅。Chrome 150 六月进稳定版的 text-fit 属性,把这件事收进了 CSS:它按容器框宽自动缩放文字节点的字号,渲染层缩放、计算值不动,不需要脚本、不需要 transform: scale() 那一堆补丁。
先说清楚它和 clamp()、vw 的区别。clamp() 和视口单位解决的是「随视口线性变化」,你不知道文字实际排出来占多宽;text-fit 解决的是「按测量结果反推」,浏览器自己量完文字、把它缩到刚好填满容器宽度。官方在 Chrome 150 的发布说明里给的原话是「缩放文字节点的字号,使其完全适应其容器框的宽度」——这是排版层的自适应,不是视口层的自适应。
之前那套 JS 方案长什么样
最典型的是这套:
function fitText(el, container) {
let size = 100;
el.style.fontSize = size + 'px';
// 逐步逼近容器宽度
while (el.scrollWidth > container.clientWidth && size > 12) {
size -= 1;
el.style.fontSize = size + 'px';
}
}
window.addEventListener('resize', () => fitText(title, bar));
它的问题不止是慢。scrollWidth 读取会强制同步布局(layout thrashing),每减 1px 都要重排一次;一个大标题从 100px 掉到 24px 就是 76 次强制重排。再叠上 resize 事件没做防抖,拖动窗口时这段代码每帧跑一遍。真实项目里这段逻辑往往还挂着 ResizeObserver,代码量轻松过百行。
现在用一行 CSS
.hero-title {
font-family: system-ui;
text-fit: shrink;
}
text-fit 有两个基础模式:grow 在文字比容器窄时向上放大,shrink 在文字溢出时向下缩小。上面的场景(固定宽度横栏,标题必须撑满、但也绝不能溢出)用 shrink 就够。
多行场景还能分开控制:
.headline {
text-fit: shrink consistent; /* 所有行按最宽那行统一缩放 */
}
.subhead {
text-fit: grow per-line; /* 每行独立缩放,各自填满 */
}
consistent:按最宽的一行算一个统一缩放系数,多行之间字号一致,适合标题。per-line:每行单独缩放,各自填满宽度,适合艺术字或分行标语。
还能配最小值/最大值兜底,避免缩到不可读或放到失真:
.tag {
text-fit: shrink 80% 120%; /* 缩放下限 80%、上限 120% */
}
一个容易踩的坑:text-fit 只改渲染,不改计算字号。你 getComputedStyle(el).fontSize 拿到的还是你写进去的基准值,不是视觉上生效的那个尺寸。所以别再指望用它去读「当前实际字号」——这个值本来就不在计算样式里。同理,它按容器框宽工作,容器必须先有确定宽度(width: 100% 之类),否则没有测量基准,属性等于没写。
兼容性要认清
目前只有 Chrome 150 支持,Safari、Firefox 都还在评审规范阶段(截至 2026 年 7 月 web-standards.dev 的记录,其他内核仍在 review)。所以它现在适合的场景是有明确降级路径的:给容器一个合理的基础字号,text-fit 作为渐进增强,支持它的浏览器自动撑满,不支持的浏览器退回你原来的排版。别把它当默认排版的地基——这是 2026 年下半年 CSS 里少数「能用但别依赖」的新属性。
下一个动作:打开你项目里那个靠 JS 量宽度的标题组件,把 fitText 那几十行删掉,换成 text-fit: shrink,再用 @supports (text-fit: shrink) {} 包一层做降级,然后在 Chrome 150 里拖一次窗口试试——你会看到以前每帧 76 次重排的那段逻辑,现在一行属性顶掉了。
评论区
登录后可评论。