弹层跟着锚点走,但锚点滚出视口了弹层还在原地——这个坑我踩了三年,今天 CSS 自己会追了

弹层跟着锚点走,但锚点滚出视口了弹层还在原地——这个坑我踩了三年,今天 CSS 自己会追了。


配过 anchored tooltip 或 popover 的同学,大概都遇到过这个场景:锚点按钮在一个可滚动的容器里,用户滚动了,弹层还钉在原地,因为它不在滚动容器内部,浏览器不知道要跟踪。

这个问题我踩了三年,每次都要靠 JS 监听滚动事件然后手动更新弹层位置——直到 anchor-scroll 属性出现。

anchor-scroll:让弹层追着锚点滚

这个属性的用法非常简单:给弹层设置 anchor-scroll: --my-anchor,浏览器就会自动监听指定锚点的滚动位置,让弹层跟着锚点一起滚动。

.button {
  anchor-name: --my-anchor;
}

.tooltip {
  position-anchor: --my-anchor;
  anchor-scroll: --my-anchor;
}

不需要任何 JS,不需要 scroll 事件监听,不需要 requestAnimationFrame。这行 CSS 做的事情本质上是:让弹层的”锚定坐标系”跟随锚点元素的滚动上下文一起运动。

配合 scroll-driven animations:弹层自己会随滚动”变身”

如果把 anchor-scroll 和 scroll-driven animations 结合起来,还能实现更骚的效果——弹层的尺寸随着锚点滚动而变化

比如一个”阅读进度胶囊”,它锚定在页面顶部,但宽度要随着用户向下滚动从 0% 逐渐撑开到 100%:

.progress-capsule {
  anchor-name: --reading-progress;
  anchor-scroll: --reading-progress;
  width: calc-size(
    anchor-size(--reading-progress, self) * 
    (scroll(nearest) - 0) / 100,
    min
  );
}

这个写法的逻辑是:弹层的基准宽度 = 锚点元素自身的宽度(anchor-size(..., self)),然后乘以滚动进度(scroll(nearest) 返回 0~100 的滚动百分比),最后用 calc-size() 让宽度计算结果生效。锚点滚多长,弹层就长多宽。

更直观一点的写法是用 @keyframes + animation-timeline: scroll()

@keyframes capsule-expand {
  from { width: 8px; }
  to   { width: calc(100vw - 32px); }
}

.progress-capsule {
  anchor-name: --reading-progress;
  anchor-scroll: --reading-progress;
  animation: capsule-expand linear both;
  animation-timeline: scroll(nearest block);
}

弹层从 8px 宽,随着滚动逐渐撑开到屏宽减去边距,整个过程零 JS。

为什么 JS 方案一直是次优解

传统 JS 方案的问题是:滚动事件发生在主线程,而弹层位置更新也要走主线程。当页面滚动很频繁时,监听器回调和布局计算会相互抢占资源,导致弹层”跟不上”——看起来就是弹层和锚点之间有延迟或跳变。

anchor-scroll 的实现是在浏览器的滚动处理线程里直接更新锚定关系,不经过主线程,所以不存在这个问题。CSS 把它原生化了之后,相当于把滚动和锚定绑定变成了浏览器的内置行为,而不是开发者手写的回调逻辑。

浏览器支持现状

anchor-scroll 是 CSS Anchor Positioning 的一部分,Chrome 125+ 和 Edge 125+ 支持。Firefox 和 Safari 支持中。整体还需要 @supports 降级:

@supports (anchor-scroll: --test) {
  .tooltip {
    anchor-scroll: --my-anchor;
  }
}

怎么判断要不要用

如果你的弹层锚点在一个会滚动的容器里,而你现在的解决方案是 JS 监听滚动事件手动更新位置——这行 CSS 值得一试。它不只是省 JS,关键是解决了 JS 方案里”弹层跟不上滚动”的体验问题。

下一步建议:在本地跑一个锚点 + 弹层的场景,给弹层加上 anchor-scroll,然后快速滚动容器,观察弹层和锚点之间有没有跳变或延迟。如果以前被这个体验问题折磨过,现在就是换方案的时候了。

评论区

0 条评论

登录后可评论。

小鹿·界面实验室 395 阅读