stroke-dasharray 是 SVG 中定义描边虚线长度与间隔的属性,配合 stroke-dashoffset 可实现文字轮廓“绘制”动画;它不作用于 HTML 文字,仅对 SVG 的 <text> 元素有效,需结合 getTotalLength() 动态获取路径长度并用 transition 驱动 offset 变化。

stroke-dasharray 是什么,为什么它能动文字
它不是给文字加动画的“开关”,而是把文字轮廓当成一条虚线路径来控制——stroke-dasharray 定义虚线长度和间隔,stroke-dashoffset 控制这条虚线从哪开始画。当文字描边(stroke)存在、且填充(fill)设为透明时,你看到的“填充动画”,其实是描边从无到有地“画”出来。
常见错误现象:text-fill-color: transparent 忘了加,或 stroke 颜色和背景混在一起看不出来;动画跑完就停在“全描边”状态,没还原成“空”效果。
使用场景:Logo 文字入场、按钮悬停强调、数据卡片标题浮现
- 必须用
svg或带-webkit-text-stroke的纯 CSS 方案(后者不支持动画 offset) - 真·可动画的只有 SVG 中的
<text>元素 +stroke-dasharray+stroke-dashoffset - 纯 HTML 文字用
-webkit-text-stroke只能配合opacity或transform做淡入/缩放,无法做“描边绘制”感
SVG 文字中怎么算 stroke-dasharray 的值
不能手填固定数字——每段文字总路径长度不同,getTotalLength() 是唯一靠谱方式。直接写死 stroke-dasharray: 100 在不同字体、字号、字母下会错位甚至卡半截。
立即学习“前端免费学习笔记(深入)”;
性能影响:每次调用 getTotalLength() 会触发重排,别在动画帧里反复调;兼容性上,IE 不支持该方法,但 IE 本身不支持 stroke-dasharray 动画,可直接忽略。
实操建议:
- 用
useEffect(React)或DOMContentLoaded(原生)获取一次长度,存为变量 -
stroke-dasharray设为该长度,stroke-dashoffset初始也设为该长度(即完全隐藏) - 动画只需改变
stroke-dashoffset从「长度」→「0」 - 示例关键代码:
const textEl = document.querySelector('text');<br>const len = textEl.getTotalLength();<br>textEl.style.strokeDasharray = len;<br>textEl.style.strokeDashoffset = len;<br>textEl.style.transition = 'stroke-dashoffset 0.6s ease-out';<br>textEl.style.strokeDashoffset = 0;
为什么用 transition 比 animation 更稳
因为 @keyframes 里没法动态读取 getTotalLength(),而 transition 能响应 JS 设置的内联样式变化,天然适配不同文字长度。硬写 animation 会导致所有文字共用一套时间点,长单词还没画完、短单词已卡住。
容易踩的坑:
- 忘了加
stroke-linecap: round,转角处描边断开,动画像“跳帧” - CSS 中写了
transition,但 JS 改的是 class 而非 style,导致过渡不触发 - 文字用了
font-weight: bold却没同步更新stroke-width,粗体描边溢出、边缘糊 - 动画结束后
stroke-dashoffset留在 0,后续重复触发需先重置回初始值
非 SVG 场景下怎么近似实现
如果只能用普通 <div> + <span>,别硬套 stroke-dasharray——它对 HTML 文字无效。可用两个叠层模拟:
- 底层文字设
color: transparent+-webkit-text-stroke: 2px #000 - 上层同内容文字设
overflow: hidden+width: 0+transition: width - 动画时展开上层宽度,像“帘子拉开”露出底层描边文字
- 缺点:不是真正路径绘制,没有圆角起笔/收笔,也不支持斜体或复杂字体微调
复杂点在于:所有方案都依赖文字渲染后的真实尺寸。哪怕用 ResizeObserver 监听,也要等字体加载完成(document.fonts.load)再计算,否则 getTotalLength() 返回 0 或 NaN。










