应使用同一SVG容器内两个path元素,通过visibility、opacity和transform组合过渡动画,并添加translateZ(0)启用硬件加速,避免display切换或混用layout属性。
直接用 transform: rotate() 或 scale() 做图标变形,视觉上容易“跳”,真正平滑的关键在于:图标的原始结构必须是同一 SVG 元素或同一组 DOM 节点,不能靠切换不同图标(比如先删 + 再插入 −),否则 transition 会失效。
推荐用单个 <svg> 或含两个 <path> 的容器,通过 class 控制 visibility + transform 组合动画:
opacity: 0 和 transform: scale(0.8),再用 transition: opacity 0.2s, transform 0.25s 同步过渡display 或 visibility 做 transition —— 它们不可动画+/−),得包在同一个 <span> 里,用伪元素 + content 切换,但需配合 font-size 和 transform: translateY() 对齐,否则上下抖动常见原因是浏览器没把该元素提升为独立图层,导致 transform 变化被迫走主渲染线程。解决方法很明确:
transform: translateZ(0) 或 will-change: transform
width、left),只保留 transform 和 opacity
will-change 处理不稳定,优先用 translateZ(0)
DOM 更新和样式计算有微小延迟,直接 element.classList.toggle('active') 后立刻读取 offsetHeight 可能拿不到新样式。实际做图标翻转动画时:
立即学习“前端免费学习笔记(深入)”;
getComputedStyle(element).transform 检查当前状态比依赖 class 名更可靠data-icon="plus",避免 class 切换竞态click 回调里连续多次 classList.toggle —— 浏览器可能合并渲染,导致中间态丢失requestAnimationFrame 包一层 class 切换,确保进下一帧再触发动画CSS 无法直接动画 d 属性,所以「加号→减号」若用 path 描述,就不能只靠 transform。这时候有两个务实选择:
<path>,一个加号一个减号,用 opacity + transform 交叉淡入淡出 —— 兼容性好,可控性强<animate>(SMIL)直接插值 d,但 Chrome 已弃用,Firefox 限制严格,不推荐新项目使用GSAP 或 svg-morpheus 这类库,它们内部用 requestAnimationFrame + 路径点插值,比 CSS 更准多数场景下,用两套 path + transform/opacity 过渡,已经足够自然,也最省心。