WWDC Quick Look 💓 By SwiftGGTeam
What’s new in Safari and WebKit

What’s new in Safari and WebKit

观看原视频

Highlight

Safari 19 引入 scroll-driven animations、cross-document view transitions 和 anchor positioning,让滚动联动动画、跨页过渡、弹层定位都可以纯 CSS 实现。


核心内容

做一个滚动进度条,过去得监听 scroll 事件再用 JavaScript 改 transform。Saron Yitbarek 在 Session 开头点出问题:JavaScript 能做的事很多,但只要能省掉,用户就能拿到更好的性能和续航(03:24)。Safari 19 把这件事推到了 CSS 里。

她以「A-School of Code」站点为例,演示了四类新能力。动画上,scroll-driven animations 让 CSS 直接绑到滚动进度,view transitions 让同源页面跳转时拥有平滑过渡。布局上,anchor positioning 把弹层、菜单这类常见交互所需的定位计算交给浏览器。视觉上,background-clip: border-area 让边框可以填渐变,shape() 函数取代 path() 写出响应式形状。媒体上则补齐了 SVG favicon、HDR 图片和 Ogg Opus/Vorbis。


详细内容

Scroll-driven animation 的最小例子06:18)。把页脚的 ::after 伪元素做成进度条,用 animation-timeline: scroll() 把动画绑到滚动容器:

footer::after {
  content: "";
  height: 1em;
  width: 100%;
  background: var(--yellow);
  left: 0;
  bottom: 0;
  position: fixed;
  transform-origin: top left;
  animation: progress-scale linear;
  animation-timeline: scroll();
}

@keyframes progress-scale {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

关键点:

  • position: fixed + bottom: 0 把进度条钉在视口底部。
  • transform-origin: top left 决定缩放从左侧开始。
  • animation: progress-scale linear 声明动画曲线为线性,时长不写。
  • animation-timeline: scroll() 把时间轴换成最近滚动容器的滚动进度,必须写在 animation 之后才能覆盖默认时间轴。
  • @keyframes 仍然是熟悉的 CSS,从 scaleX(0)scaleX(1) 横向铺满。

view() 时间轴 + animation-range12:20)。当动画希望随元素进出视口而触发时,用 view() 而不是 scroll()

.topic-item {
  animation-fill-mode: both;
  animation-timeline: view();
  animation-range: 0% 50%;
  &:nth-child(3n + 1) { animation-name: in-from-left; }
  &:nth-child(3n + 2) { animation-name: in-from-middle; }
  &:nth-child(3n + 3) { animation-name: in-from-right; }
}

关键点:

  • animation-fill-mode: both 让元素在动画前后都保留对应帧的样式。
  • animation-timeline: view() 把进度绑到「元素相对视口」的位置。
  • animation-range: 0% 50% 限定动画只在元素从进入视口到滚到视口一半的过程中播放,剩下的滚动里元素就静止可读(12:00)。
  • :nth-child(3n + 1/2/3) 让三列分别命中三种不同的 @keyframes,做出错落的入场效果。

Cross-document view transitions14:20)。同源页面之间的导航,最少只要一行:

@view-transition {
  navigation: auto;
}

@media not (prefers-reduced-motion) {
  @keyframes slide-in {
    from { translate: 100vw 0; }
  }
  @keyframes slide-out {
    to { translate: -100vw 0; }
  }
}

关键点:

  • @view-transition { navigation: auto } 在两个页面都加上,浏览器就会自动给同源跳转套上淡入淡出。
  • @media not (prefers-reduced-motion) 把自定义滑动效果包起来,尊重系统的「减少动态效果」开关(16:00)。
  • 给具体元素声明 view-transition-name,再用 ::view-transition-old(...)::view-transition-new(...) 控制各自的进出动画。

Anchor positioning21:5823:25)。配合 popover API,菜单从此不用 JS 算位置:

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

.profile-menu {
  position-anchor: --profile-button;
  position-area: top right;
}

关键点:

  • anchor-name: --profile-button 把按钮注册为锚点,名字是 CSS 自定义标识符。
  • position-anchor: --profile-button 让菜单选定锚点,建立绑定关系。
  • position-area: top right 用关键字声明菜单出现在锚点的右上区域;可换 bottom centerspan-rightspan-left 等值。
  • 需要更细粒度时改用 top: anchor(bottom); left: anchor(left)anchor() 函数返回锚点对应边的坐标,可以放进 calc()28:26)。

border-area 渐变边框31:05):

.primary-btn {
  background: border-area linear-gradient(to bottom right in hsl, yellow, orange);
  border-color: transparent;
}

关键点:

  • background 简写中的 border-area 关键字让背景图延伸到边框区域。
  • border-color: transparent 把边框本色挖空,显示底下的渐变。
  • 这是过去用伪元素或 SVG 才能做出的渐变描边的纯 CSS 写法。

核心启发

  1. 把滚动进度条迁回 CSS:站点页脚或文章顶部的进度条,用 animation-timeline: scroll() 替换原有的 scroll listener。值得做的理由是去掉 JS 后主线程不再被滚动事件唤醒,移动端续航和帧率都更稳。开始的方式:先在 Safari Technology Preview 打开演示页面,把现有进度条组件改写成 position: fixed 伪元素,再加上一行 animation-timeline: scroll() 验证。

  2. 用 view() + animation-range 做卡片入场:列表、卡片网格、长文档的小标题区,过去用 IntersectionObserver 触发动画。值得做的理由是 view() 把「元素相对视口的位置」直接喂给动画进度,反向滚动时还能自然回放。开始的方式:选一个现有的 IntersectionObserver 入场动画,删掉 JS,改用 animation-timeline: view(),把 animation-range 设成 0% 50% 让动画只占视口前半段,确保信息可读。

  3. 用 anchor positioning 替换 popper.js:所有依赖 popper.js / floating-ui 算位置的下拉菜单、tooltip。值得做的理由是 anchor-name + position-area + position-try 把翻转逻辑交给浏览器,包体积、bug、键盘焦点都顺带解决。开始的方式:挑一个最简单的头像菜单,HTML 加 popover + popovertarget,CSS 给按钮 anchor-name、给菜单 position-anchorposition-area: bottom center,再用 @supports 给老浏览器留旧路径。

  4. prefers-reduced-motion 当作硬约束:所有 scroll-driven 和 view-transition 代码都包进 @media not (prefers-reduced-motion)。值得做的理由是 Saron 在 Session 中明确把 motion discomfort 列为常见症状,且包含眩晕、恶心等真实生理反应。开始的方式:在团队 CSS lint 里加一条规则,凡 animation-timeline@view-transition 的所在选择器必须出现在 prefers-reduced-motion 媒体查询内,否则告警。


关联 Session

评论

GitHub Issues · utterances