凛ノブログ

Eat, Sleep & Daydream

Anchor 初体验:实现一个可追随的 Navbar 滑块

我在 blog 上一个 theme 折腾导航栏的时候,为了实现那个“滑块跟随鼠标”的效果,跟 CSS 里的 nth-child 还有 translateX 较劲了半天。

偏移百分比和像素是手动调式出来的。要是按钮里的文字长短不一,那简直灾难。

手算偏移量

这是我以前最常用的办法。给导航栏加个伪元素当背景,然后鼠标指到哪个,就手动把背景“挪”过去。当然这类效果用 JS 其实更好实现,但个人的强迫症,我是不会在导航上引入 JS 的。

代码写起来大概长这样:

.navbar::before {
  width: 60px;
  transition: transform 0.3s;
}

.navbar:has(a:nth-child(1):hover)::before { transform: translateX(0); }
.navbar:has(a:nth-child(2):hover)::before { transform: translateX(64px); }

每增加一个导航链接,就得去重新算一遍偏移量。之前导航还有图标 + 响应式设计,手机上没图标就露馅了,为了动效把图标砍了。

Anchor

用上 Anchor Positioning 之后,逻辑变得非常简洁。滑块只需要通过声明式语言指定它的锚点目标即可。

这种方式让定位逻辑回归到了最直觉的状态:你只需要声明“要跟谁对齐”,剩下的适配工作交给浏览器原生处理。

比如下面 .indicator 声明了 position-anchor: --nav-anchor

<nav class="navbar">
  <a class="active">首页</a>
  <a class="px-6">超长字段的友链</a>
  <a>关于</a>
  
  <div class="indicator"></div>
</nav>

<style>
  .active { anchor-name: --nav-anchor; }
  a:hover { anchor-name: --nav-anchor; }

  /* 当有任意一个链接被 hover 但不是 .active,.active 就暂时失去锚点名 */
  :has(a:hover:not(.active)) .active { anchor-name: none; }

  .indicator {
    position: absolute;
    position-anchor: --nav-anchor;
    
    left: anchor(left);
    right: anchor(right);
    top: anchor(top);
    bottom: anchor(bottom);
    
    transition: all 0.3s ease;
  }
</style>

按钮里的文字不管多长,anchor(right) 都能精准映射到边缘,滑块也因此实现了自动适配。

总结

在以往的方案中,依赖 JavaScript 监听尺寸变化,而现在,我们可以直接使用声明式语言定义元素间的逻辑关联,将复杂的坐标计算交给浏览器的渲染引擎处理。

浏览器支持

最新浏览器已经基本支持了,Firefox 似乎还有点问题。

Data on support for the {feature} feature across the major browsers from caniuse.com

本作品采用知识共享署名-非商业性使用-相同方式共享 (CC BY-NC-SA) 协议进行许可。
由于是静态页面,评论提交后不会立即显示,这里 查看提交的评论。