动画类名必须是--modifier并绑定具体Block(如.dialog--fade-in),否则CSS不生效、JS失锚、工具链识别失败;.fade-in等独立类名被识别为新Block,语义缺失且权重不足,无法精准控制动画逻辑。

动画类名必须是 --modifier,绑定到具体 Block(如 .dialog--fade-in),否则 CSS 不生效、JS 控制失锚、工具链识别失败。
为什么不能用 .fade-in 这类独立类名
这类写法被 Stylelint 和 PostCSS 插件识别为新 Block,不是修饰符,IDE 类名跳转失效;CSS 权重也压不过 .dialog__content 这类元素选择器,样式大概率被覆盖。更关键的是语义缺失:加在 .dialog 上可能缺 z-index,加在 .toast 上又可能漏位移起点。
正确命名必须带上下文:.dialog--fade-in 表示「这个对话框正处于淡入流程中」,所有动画逻辑(起点、硬件加速、事件监听)都围绕它展开。
transition 和 animation 必须显式写在修饰符规则里
只加 .dialog--visible 类,但 CSS 里没写 transition: opacity 0.2s ease 或 animation: dialog-fade-in 0.3s ease,动画就完全不触发。浏览器不会继承父级的 transition,也不会自动补全属性。
常见错误包括:
-
.dialog基础类没定义transition: opacity 0.2s ease, visibility 0.2s step-end,只在.dialog--visible里改opacity: 1→ 隐藏时直接跳变 - 用
animation却漏了@keyframes名称对齐:@keyframes dialog-fade-in必须和.dialog--fade-in中的animation: dialog-fade-in 0.3s ease完全一致 - 误用
* { transition: all .2s }→margin、color等非预期属性也动,且阻断will-change优化
多个动画状态如何共存且不嵌套
BEM 明确禁止 .dialog--fade-in--scale 或 .dialog--loading--disabled 这种链式修饰符。它们不是父子关系,而是并列布尔开关,必须平级添加、独立控制。
正确做法是:
立即学习“前端免费学习笔记(深入)”;
- HTML 中写:
class="dialog dialog--fade-in dialog--scale" - CSS 中分别定义:
.dialog--fade-in .dialog__overlay { animation: dialog-fade-in 0.25s ease; }和.dialog--scale .dialog__content { transform: scale(1.02); } - JS 控制用
el.classList.toggle('dialog--fade-in')和el.classList.toggle('dialog--scale')分开操作 - 同一元素频繁切换时,先调用
el.getAnimations().forEach(a => a.cancel())清理旧动画,避免竞态
JS 添加/移除类名必须匹配动画生命周期事件
类名加完立刻移除,动画根本播不完;监听 animationend 却不检查 event.animationName,可能误删其他动画的类。
关键不是“怎么加”,而是“什么时候加、什么时候收”:
- 入场动画:
el.classList.add('dialog--fade-in')→ 监听animationend→ 检查event.animationName === 'dialog-fade-in'→ 再清理 - 退出动画:先
add('dialog--fade-out'),等animationend后再remove('dialog--fade-in'),确保 CSS 能触发退出过渡 - 修饰符语义必须和组件强绑定:
.dialog--fade-in挪到.drawer上就失效;硬件加速条件、取消逻辑、事件过滤全都依赖这个绑定成立


















