配了八年 View Transitions,今天发现它最麻烦的地方终于要被 CSS 自己接管了——大规模元素命名这件事,浏览器自己要会了
写过 View Transitions 的人,大概都遇到过这个问题——教程里那个优雅的卡片从列表飞入详情页的动画,看完觉得”这也太简单了”,然后一打开自己的商品列表页,48 张卡片,每张都要飞,脑子就卡住了。
根子在一个很小的东西上:每个参与过渡的元素,都必须有一个独一无二的 view-transition-name。一张卡片可以用 view-transition-name: card。48 张呢?不行,会冲突。你得给每张卡片的 name 都带上唯一 ID:card-1、card-2、card-48。
所以你就开始写循环,在 JS 里动态塞 view-transition-name。这是今天之前唯一的解法。
浏览器自己要学”数数”了
sibling-index() 就是干这件事的。
这是一个 CSS 函数,它返回元素在自己父级里的顺序位置——第几个,就是几。不需要你往 HTML 里塞 data-id,不需要 JS 循环,浏览器自己知道。
.card {
/ 第一张卡片 index 是 1,第二张是 2,以此类推 /
animation-delay: calc(sibling-index() * 80ms);
}
这个函数已经在 Chrome 138(2025 年中旬)落地了,配合 calc() 可以做很多事情:错开动画节奏、自动算进度条位置、等分布局宽度。Smashing Magazine 那篇《Advanced Tree Counting》把它和 sibling-count() 配对用,做出了完全不用 JS 的阶梯动画和动态进度指示器——浏览器自己知道”我在第几个,共几个”。
但这个函数在 View Transitions 里有个特殊价值:它可以用来自动生成唯一的 view-transition-name。
那个缺失的另一半,终于有眉目了
sibling-index() 会数数了,但只靠它还不够。你可以用它算出”第几个”,但 view-transition-name 需要的不是一个数字,而是一个字符串——比如 card-1、card-2。
Chrome 团队工程师 Bramus(也是 View Transitions 的核心推进者)为此提了一个专门的 CSS 函数:ident()。它的作用很直接:把多个值拼接成一个合法的 CSS 标识符名字。
组合起来就是这样:
.card {
/ 生成 card-1、card-2、card-3… 每个元素独一无二 /
view-transition-name: ident(“card-” sibling-index());
}
一行 CSS,10 张卡片和 10,000 张卡片用同一套规则,名字全自动生成,完全不用 JS 参与。
CSS-Tricks 那篇《Cross-Document View Transitions: Scaling Across Hundreds of Elements》把这个组合称为”纯 CSS 的梦想”,因为它同时解决了两件事:不再需要往 HTML 里手动塞唯一标识,也不再需要 JS 循环去动态赋值。
而且不只 view-transition-name,这个思路可以平移到所有需要唯一标识符的 CSS 属性上:scroll-timeline-name、container-name、view-timeline-name,哪里需要唯一名字,一行解决。
现在能用到什么程度?
sibling-index() 今天已经在 Chrome 138+ 和 Safari 26+ 上可用,Firefox 还在实现中。可以直接用 progressive enhancement 的思路:
.card {
animation: fade-in 400ms ease both;
/ 基础版本:所有卡片同时入场,兼容所有浏览器 /
}
@supports (top: calc(1px sibling-index())) {
.card {
/ 支持 sibling-index() 的浏览器:自动错开 /
animation-delay: calc((sibling-index() – 1) 80ms);
}
}
对于 View Transitions 的命名问题,ident() 本身还没在任何浏览器里落地,目前只有 Chrome 的 Intent to Prototype(2025 年 5 月),但这个信号很重要——它在 W3C CSSWG 的讨论已经进入实质性推进阶段。
在那之前,如果你现在就要解决大规模 View Transitions 命名问题,可以参考 CSS-Tricks 那篇文章里提到的”刚好在用时”命名策略:不在页面加载时给所有元素分配 view-transition-name,而是在 pageswap 事件里、用户真正点击某张卡片的那一刻,才给那一张卡片动态分配名字,动画结束后清理掉。这样浏览器只处理实际参与过渡的那几个元素,而不是快照整页的几十个。
结论
view-transition-name 的唯一命名这个问题,今天靠 JS 循环解决是可行的,但不是最终答案。sibling-index() 把”数数”还给了 CSS,ident() 把”起名字”也还给了 CSS,两者组合,意味着一个声明解决 10 个元素的过渡还是一个声明解决 10,000 个——只是元素多了,名字自动长出来。
等 ident() 正式落地,这件事就从”工程问题”彻底变成”一行 CSS”。
可落地的下一步:
- 把现有的 View Transitions 里的 JS 循环命名逻辑抽出来,用 pageswap/pagereveal 事件做”刚好在用时”命名,减少不必要的快照开销
- 在自己的卡片组件里加一行 @supports (top: calc(1px * sibling-index())) 的渐进增强,先把入场动画的 JS 省掉
- 关注 CSSWG 对 ident() 的推进进度——一旦有浏览器开始实验,就是大规模铺开的时候
评论区
登录后可评论。