前端知库

@starting-style 和离散属性过渡解决了什么问题?

解释新元素为什么无法从 display:none 过渡到可见状态,说明 @starting-style、allow-discrete 与 opacity/transform 的配合方式,以及退出动画、DOM 时机、兼容性和渐进增强边界。

参考答案

简答

  • 新插入的元素或从 display: none 变为可见的元素,浏览器只看到最终样式,没有起点,所以 transition 不播放。
  • @starting-style 为“没有前一帧样式”的元素声明过渡起点,替代过去“先加类、下一帧再删类”的 JavaScript 时序。
  • display 是离散属性,不能插值;transition-behavior: allow-discrete 让它在合适时刻切换,退出时 opacity 等动画才有机会播完。
  • 动画只是表现层,焦点、inert、资源清理仍由组件状态负责;不支持时内容照常显示,只是没有动画。

首次插入为什么没有起始帧

传统写法依赖 JavaScript 等一次渲染再移除初始类:

JavaScript
document.body.append(notice); // notice 带 notice--entering 类
requestAnimationFrame(() => notice.classList.remove('notice--entering'));

组件重渲染、批量插入或 SSR 激活都可能打乱这段时序。用 @starting-style 改由 CSS 声明起点:

CSS
.notice {
  opacity: 1;
  transform: none;
  transition: opacity 180ms ease, transform 180ms ease;
}

@starting-style {
  .notice {
    opacity: 0;
    transform: translateY(8px);
  }
}

起始规则只在元素第一次获得样式时生效;元素已存在、只是修改属性时,仍由普通 transition 处理。

进入和退出都要处理 display

CSS
.dialog {
  display: none;
  opacity: 0;
  transform: translateY(12px) scale(0.98);
  transition:
    opacity 180ms ease,
    transform 180ms ease,
    display 180ms allow-discrete;
}

.dialog[open] {
  display: block;
  opacity: 1;
  transform: none;
}

@starting-style {
  .dialog[open] {
    opacity: 0;
    transform: translateY(12px) scale(0.98);
  }
}

进入时 display 立刻切到 block,再从 starting-style 过渡;退出时 display: none 会推迟到动画结束才生效。模态框本身仍应使用原生 <dialog> 的 showModal(),这段 CSS 只负责视觉。

退出动画不能承担业务清理

transitionend 可能因为属性被再次修改、元素被移除、页面进入后台或减少动态效果而不触发。释放监听、定时器和焦点应由组件状态在进入关闭阶段时完成,不要等动画事件。

淡出期间元素仍在页面上,要同步禁止交互:

CSS
.toast[data-closing='true'] {
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .toast,
  .dialog {
    transition-duration: 1ms;
    transform: none;
  }
}

pointer-events: none 不影响键盘焦点和读屏,还需要配合 inert 和 ARIA 状态。SSR 首屏已经处于最终样式的内容,通常不应再播放入场动画。

兼容与渐进增强

CSS
@supports (transition-behavior: allow-discrete) {
  .notice {
    transition: opacity 180ms ease, display 180ms allow-discrete;
  }

  @starting-style {
    .notice {
      opacity: 0;
    }
  }
}

支持范围以 MDN 兼容表为准。验收覆盖插入、关闭、快速重复打开、减少动态效果,以及不支持时内容、关闭按钮和焦点是否仍然正常。