✍️ 笔顺演示功能介绍

字帖生成器 v3.0.7 · 离线笔画笔顺动态演示(双图层重构 + 暂停/恢复 + 触屏/平板自适应 + 可视视口精确居中)

更新日期:2026-10-04 · 适用版本:v3.0.7 及以上

← 返回字帖主页

一、功能概述

新增v2.9.8 起字帖支持离线汉字笔画笔顺动态演示。点击字帖中任意汉字方格,即弹出标准窗口式演示弹窗,逐笔演示笔顺动画。

二、入口位置与开关

"笔顺演示"控件位于字体下拉菜单下方的快捷工具栏,与"刷新""打印"按钮平齐:

提示:关闭后点击字格不会弹出弹窗,但字帖中的笔画数、笔顺 SVG 仍然正常显示(离线数据始终可用)。

三、弹窗使用说明

3.1 演示舞台

弹窗中央的方形区域为演示舞台,背景始终为白色(即使 dark 模式也保持白色,确保黑色笔画清晰可读)。

永氵

↑ 未点击"播放"时:汉字以 0.25 不透明度黑色轮廓显示

3.2 播放控制(v2.9.8 暂停/恢复 + v2.9.8 双图层架构)

v2.9.8 播放按钮支持播放/暂停两态切换:

v2.9.8 采用双图层架构,确保 0.25 不透明度的汉字轮廓在演示过程中始终显示(位于底层,被笔画遮挡而非消失):

3.3 速度调节

速度滑块位于播放按钮下方:

3.4 颜色规则

元素 颜色 不透明度
普通笔画 #000000(黑色) 未播放 0.25 / 播放时 1.0
偏旁部首 #d32f2f(朱砂红) 未播放 0.25 / 播放时 1.0

偏旁部首的笔画索引由数据中的 radStrokes 字段自动识别。如该字段缺失,则所有笔画统一为黑色。

3.5 窗口操作(v2.9.8 修复最小化/最大化)

操作 方式
最小化 点击标题栏右侧 ▱ 按钮,隐藏主体仅保留标题栏;同时记录当前窗口位置/尺寸
最大化 点击标题栏右侧 ▢ 按钮:从最小化恢复时精确还原最小化前的位置与尺寸;非最小化状态下切换最大化(放大/还原)
关闭 点击标题栏右侧 ✕ 按钮,或按 Esc 键关闭最上层弹窗
拖拽 按住标题栏拖动(最大化状态不可拖拽);拖拽范围限制在屏幕内,窗口不会被拖出屏幕外(v3.0.4)
复位排列 双击标题栏,把自由拖拽的窗口复位回自动排列(v3.0.4)
聚焦 点击相同汉字的字格,已有弹窗会置顶并闪烁高亮(v3.0.4 补齐闪烁样式,触摸端点按有可见反馈)
v2.9.8 关键修复:弹窗固定宽高比与汉字(1:1 舞台)匹配;最小化→最大化时位置和尺寸与最小化前完全一致,不发生偏移。

3.6 多弹窗管理

3.7 触屏 / 平板自适应(v3.0.4)

v3.0.4 重构了弹窗的排列与触屏交互,桌面端行为刻意保持不变:

说明:打印隐藏、最小化 / 最大化 / 关闭、Esc 关闭最上层弹窗、"减少动态效果"偏好等既有行为全部保留。

3.8 「总在可视区正中央」+ 多窗口优雅排布(v3.0.6)

v3.0.6 修掉了 pad / 手机上两类长期存在的排布缺陷。根因不在 CSS 写得对不对,而在"以哪个视口为基准":

实测取证(scripts/measure-popup-layout.cjs,只读测量、不改产品代码):7 种设备(桌面 1920×1080 / 笔记本 1280×800 / 平板横竖 1180×820、820×1180 / 手机 390×844、844×390、360×640)× 1–4 窗口,另加 2× 页面缩放场景(用 CDP Emulation.setDeviceMetricsOverride 造出稳定的"布局视口 ≠ 可视视口"稳态),并在同一页面里做 A/B 对照:

场景 修复后偏差 纯 CSS 方案(对照列)偏差
网格排列(28 组:7 设备 × 1–4 窗口) 最大 0.1px(几何稳定 28/28,可疑 0) —
2× 页面缩放(10 组) 0, 0(可视视口确已分离 10/10,几何稳定 10/10,可疑 0) 最大 387.8px 横向(phone-360x640)/ 806.8px 纵向(phone-390x844)
怎么读这张表:「对照列」是把 JS 写入的内联 left/top 清掉、退回旧的纯 CSS left/top:50% 后当场量出来的,不是推算值。它的数值随文档高度浮动(布局视口高度取决于当时内容高度),所以它是一个量级证据——说明不修就会偏出几百像素;而「修复后 = 0」那一列才是逐次可复现的判据。
关于这套判据本身(两处自我修正,都是本轮抓到的):
  1. 假阳性:稳定性判据原是「连续 2 次采样一致」(240ms 静止),实测会在 desktop 单窗口采到一个平台期——overlay 的内联 top 已经是正确值,但窗口还在插槽内做入场收尾,包围盒中心偏 7.6px,再等 1500ms 自行回到 0。也就是产品没错、关口采早了。已把判据抬到连续 6 次(600ms 静止)。
  2. 负控曾把渲染进程跑崩:MEASURE_STABLE_TIMEOUT=1 会连带抹掉轮询等待,于是开/关窗节奏被拉满,Chromium 抛 detached Frame。退出码确实是 1,但那是「崩了」而不是「判据咬人了」,负控等于失效。已加固定沉降 MEASURE_SETTLE_MS=700 与轮询解耦;修完实测 detached Frame 0 次、28/28 + 10/10 如实判为 UNSTABLE、退出码 1。
另踩过一次坑:早先稳定性判据写成在页面里调用另一个 Node 侧函数,而 page.evaluate 只序列化传入的函数本身,于是每次都抛 ReferenceError 又被 catch 吞掉,伪装成"几何不稳定",整份报告 28/28 全红且原因不可见。判据自己也要能被证伪;而「退出码非 0」不等于「负控生效」,必须看输出差分。

四、离线数据方案

4.1 数据来源

笔画数据来源于 MakeMeAHanzi 数据集,本项目内置 9574 个汉字的完整笔画信息(SVG 路径 + 中心线 + 偏旁索引)。

4.2 数据格式

每个汉字的数据结构:

{
  "strokes": ["M 518 382 Q 572 385 ...", ...],   // SVG path d 字符串数组,长度即笔画数
  "medians": [[[121, 393], [193, 372], ...], ...], // 每笔中心线坐标
  "radStrokes": [0, 1, 2]                          // 可选:偏旁笔画索引
}

4.3 压缩与加载

方案 文件 大小 说明
主数据源 /hanzi-data/hanzi-data.bin 11.75 MB Gzip 二进制,Web Worker 后台解压
降级备用 /hanzi-data/hanzi-data-embedded.js 15.7 MB Base64 + Gzip,file:// 协议时使用

4.4 查询优先级

当点击字格查询汉字数据时,按以下顺序查找:

  1. 自定义数据(_customDataMap)— 用户手动添加的冷僻字 / 新造字,优先级最高
  2. 内置数据(_charDataMap)— 9574 汉字离线字典
  3. 网络缓存(_networkCache)— 之前从 CDN 加载过的字符(内存缓存)
  4. 网络加载(仅 navigator.onLine === true 时)— 从 jsdelivr CDN 在线获取,成功后缓存到 _networkCache
说明:常用字(9574 之内)查询不会发起任何网络请求,全部走本地字典。冷僻字在联网时自动从 jsdelivr CDN 备选加载并缓存到内存,离线时仅使用本地数据。

4.5 持久化与清理

自定义数据通过 localStorage 持久化(key: calligraphy_custom_hanzi):

五、可扩展性(冷僻字 / 新造字)

支持运行时通过代码插入冷僻字 / 新造字数据:

import { setCharData, persistCustomData, buildCharData } from './src/modules/hanziDataStore.js';

// 构造规范化数据(自动校验)
const data = buildCharData(
    ["M 100 500 L 924 500"],     // SVG path d 字符串数组
    [[[100, 500], [924, 500]]],   // 每笔中心线坐标
    undefined                     // 可选:偏旁笔画索引
);

// 插入并持久化
setCharData('㐀', data);
persistCustomData();

坐标系必须为 MakeMeAHanzi 规范(X 轴 0 ~ 1024,Y 轴 -124 ~ 900,向下为正),否则渲染会错位。

六、键盘快捷键

按键 作用
Esc 关闭最上层演示弹窗

七、技术实现

详细数据结构与压缩方案见 离线汉字数据方案_v2.9.8.md。

八、常见问题

Q1:为什么点击字格没有反应?

请检查工具栏"笔顺演示"按钮是否处于开启状态(橙色高亮)。若关闭则点击字格不会弹出弹窗。

Q2:弹窗显示"无'X'的笔画数据"怎么办?

该字不在 9574 内置汉字范围内,且当前处于离线状态或网络备选加载失败。请联网后重试,或通过 setCharData 接口手动添加该字数据。

Q3:弹窗舞台为什么是白色的?

舞台背景始终为白色,确保黑色笔画在任何主题下都清晰可读。dark 模式下仅弹窗边框、标题栏、控件区跟随主题变化。

Q4:为什么未播放时汉字是淡黑色?

这是设计要求:未播放时整个汉字以 0.25 不透明度的黑色轮廓显示,作为预览。点击"▶ 播放"后切换为完全不透明逐笔演示。

Q5:如何同时演示多个汉字?

依次点击不同汉字的字格,最多同时弹出 4 个弹窗。相同汉字的字格点击不会新开弹窗,而是聚焦已有弹窗(置顶 + 闪烁高亮,需手动点击"▶ 播放"才会演示)。

九、版本历史

版本 日期 主要变更
v2.9.8 2026-08-05 新增离线笔画笔顺演示弹窗、工具栏按钮、9574 字离线数据
v2.9.8 2026-08-05 按钮文字化、自动播放移除、导航介绍联动抽屉、独立介绍页面
v2.9.8 2026-08-05 双图层重构(0.25 轮廓始终显示,仅被笔画遮挡);默认速度 5x → 1x;修复最小化/最大化按钮失效;最小化→最大化精确还原位置与尺寸;弹窗固定宽高比
v2.9.8 2026-08-05 修复速度滑块失效(hanzi-writer 3.7.3 无 updateOptions);滑块改为 5 档(1x-5x);修复移动端 light 主题;修复打印黑框
v2.9.8 2026-08-06 播放速度持久化到 localStorage,后续新弹窗默认采用上次调整的速度
v2.9.8 2026-08-06 播放按钮支持播放/暂停两态切换(pauseAnimation/resumeAnimation);播放中按钮变粉色提示可暂停
v3.0.0 2026-08-06 笔画弹窗加载态+就绪徽章;AI组词补齐(双引擎:DeepSeek/火山引擎豆包);三项独立开关(全量检查/组词补齐/拼音纠错)+旋转图标进度提示
v3.0.4 2026-09-18 触屏/平板自适应重构:容器改 CSS Grid + solveLayout 求解器自动排列与缩放;手机空间不足时自动收最旧窗口为药丸(LRU 恢复);触摸目标放大(按钮 26→34px、播放 36→44px、滑块 14→22px);拖拽视口边界约束 + 双击标题栏复位;补齐聚焦闪烁样式;新增 769–1280px 平板断点;桌面端布局零退化