手写字效果是用CSS动画逐字“写出”的视觉模拟,核心方案是SVG path配合stroke-dasharray与stroke-dashoffset控制描边动画,需动态计算时长、处理Safari兼容性及截图问题。

手写字效果 ≠ 手写签名,它不依赖 canvas 绘图,而是用 CSS 动画逐字“写出”文字,本质是视觉模拟。直接用 stroke-dasharray + stroke-dashoffset 配合 path 或伪元素是最轻量、兼容性最好的方案。
为什么不能只用 text-shadow 或 font-family 模拟手写?
单纯换字体(如 "Brush Script MT", cursive)只是静态样式,没有“书写过程”;text-shadow 无法控制笔画顺序和节奏,也做不到连笔或起收笔渐变。真手写字效果必须有路径+描边+动画时序控制。
常见错误现象:
– 文字一次性弹出,毫无书写感
– 笔画断裂、错位,尤其在中文字体下
– Safari 中动画卡顿或不触发
- 中文需用
<svg></svg>+<text></text>或转为<path></path>,因为纯 CSS 的stroke对中文文本支持极差 - 英文/拉丁字母可走
clip-path或mask路径裁切,但 iOS 15.4 以下不支持mask,稳妥起见仍推荐 SVG - 避免用
letter-spacing+opacity逐字淡入——这不是手写,是打字机效果
SVG <path></path> + stroke-dasharray 是最稳的实现路径
把文字转成 SVG 路径(可用在线工具如 svg-path-editor 或 Figma 导出),再用 stroke-dasharray 匹配总长度,stroke-dashoffset 从全长变为 0 实现“写出”动画。
立即学习“前端免费学习笔记(深入)”;
关键参数说明:
– stroke-dasharray="L,L":L 是路径总长(用 getTotalLength() 获取)
– stroke-dashoffset 初始设为 L,动画终点为 0
– 必须设 fill="none",否则看不到描边动画
- 路径要尽量简化,否则移动端重绘压力大;复杂字体建议只用于标题,正文慎用
- 动画时长别固定写死,按字符数动态计算:比如每字 300ms,10 字就 3s,避免短字快、长字拖沓
- 加一点
stroke-linecap: round和轻微filter: blur(0.3px),能缓解硬边,更像真实笔触
移动端 Safari 的 stroke-dashoffset 动画失效怎么办?
Safari(尤其是 iOS 16.5–17.2)对 SVG 描边动画有渲染 bug:stroke-dashoffset 改变不触发重绘,导致动画卡住或直接跳变。
绕过方式只有两个:
- 强制重排:在动画开始前给 SVG 加个无意义的 style 变更,例如
svg.style.transform = 'scale(1)' - 改用 JS 控制:不用 CSS 动画,用
requestAnimationFrame手动更新stroke-dashoffset,虽然代码多几行,但 100% 可靠 - 绝对不要依赖
@keyframes+stroke-dashoffset在 Safari 上做关键路径动画
导出为图片时手写字迹变细或消失?
用 canvas.drawImage(svg) 或 dom-to-image 截图时,SVG 的描边动画状态不会被保留——你截到的是动画结束帧,且部分浏览器会忽略 stroke-width 缩放。
正确做法:
– 动画结束后,用 JS 把 stroke-dashoffset 设为 0,再等一帧(await new Promise(r => setTimeout(r, 0)))再截图
– 若需保留“未写完”状态导出,得提前记录当前 offset 值,手动重绘到 canvas 上,不能偷懒靠自动渲染
最容易被忽略的一点:动画结束不代表 DOM 渲染完成。Safari 下即使 offset = 0,也可能因合成层未更新而截出空白。务必加 getBoundingClientRect() 触发 layout 强制同步。



















