← CSS Animation · 能力速览 / none → block 也能动起来 待审核 4 / 22
discrete · @starting-style · allow-discrete

none → block 也能动起来

元素从 display: none 切到 display: block 时,浏览器没有「上一帧」可插值,transition 默认动不起来。两个新能力解决它:@starting-style 提供「动画开始那一刻的初始样式」,transition-behavior: allow-discretedisplay 这类离散值也能参与过渡。

1 · 为什么 display 不能「补间」?——先认识 animation type

补间(interpolation)的本质,是在起始值结束值之间求一个「中间值」:进度走到哪,就取那一处的中间态。opacity: 0 → 1 的一半是 0.5width: 0 → 100px 的一半是 50px、两个颜色的中点是它们在颜色空间里的混合——这些值都活在一段连续的数值空间里,中间值天然存在、可以算出来。CSS 把这类属性的插值方式叫 by computed value

但有些属性的取值是一组离散的关键字display 只能是 none/block/flexnone / block / flex \dotsvisibility 只能是 visible / hiddenposition 只能是 static/absolutestatic / absolute \dots。「半个 none」「blockflex 的中点」根本不存在——它们是彼此独立的标签,之间没有数值意义上的中间态。这类属性的插值方式就叫 discrete,这正是 display: none → block 默认动不起来的根本原因。

discrete 的插值规则(MDN):设进度为 pp < 0.5 用起始值,p0.5p \ge 0.5 用结束值——整段过程只在 50% 处切换一次,中间没有任何过渡。

拖下面的进度条 p 感受差异:上面那条用 left(长度,by computed value)定位,跟着 p 平滑滑动;下面那条用 justify-content(关键字,discrete)对齐,只会在 p 跨过 50% 那一刻从 flex-start 直接跳到 flex-end

1.1 · 哪些属性是 discrete?为什么?

判据就一条:这个属性的取值之间,存不存在「数值意义上的中间值」。有连续数值空间(长度 / 百分比 / 颜色 / 数值)的能求中点 → by computed value;取值是一组互斥关键字、求不出中点的 → discrete。

属性 animation type 为什么
display discrete 决定盒子是否生成、用哪种 formatting context,关键字枚举,无中间态
visibility discrete* visible / hidden 两态(*有特例,见下)
position discrete static / absolute… 是整套定位模型的切换,不能「插一半」
justify-content · text-align · flex-direction discrete 对齐 / 排版关键字,标签之间无中点
opacity by computed value 0–1 的连续数值,0.5 就是半透明
width · margin · gap by computed value 长度 / 百分比,可取任意中间值
color · background-color by computed value 在颜色空间里插值出过渡色
transform · translate · scale by computed value 数值 / 矩阵插值
自定义属性 --x 默认 discrete 默认当字符串只能跳变;用 @property 注册类型后转为可插值

两个细节:其一,visibility 名义上也是 discrete,但被特别处理——在 visible 与 hidden 之间过渡时整段保持 visible,只在结束那刻才真正隐藏,方便「先淡出、再消失」。其二,discrete 值还不可叠加(not additive),所以它们也不参与 animation-composition 的 add / accumulate(见 核心属性页的动画合成一节)。

2 · 解决:@starting-style + allow-discrete 让离散属性也能动

能力 作用
@starting-style 设置动画 / 过渡开始时的初始样式——解决「没有起始帧可插值」的问题。
transition-behavior allow-discretedisplay 等离散属性也能过渡(否则切换是瞬间的)。

厘清一个易混点:上面「50% 跳变」是 discrete 属性在 @keyframes / WAAPI 动画里的通用规则。而在 transitiondisplay 被特别处理——变为可见时在过渡开始就立刻切到 block,变为 none 时拖到过渡结束才切,正是为了给 opacity 留出完整的淡入 / 淡出时间。下面的 demo 就是这种用法。

左边靠 transition:display: none → blockallow-discrete 过渡,并在 @starting-style 里把起始 opacity 设成 0,于是淡入;隐藏时反向淡出,display 拖到过渡结束才切回 none,出场也不再瞬间消失。右边靠 animation:出现时直接跑一段 from{opacity:0} to{opacity:1}forwards 动画,天然带起始帧,不需要 @starting-style。