ByteNoteByteNote

字节笔记本

2026年2月20日

Hanzi Writer - 汉字笔画顺序动画和练习测验库

本文介绍 Hanzi Writer,一个用于汉字笔画顺序动画和练习测验的免费开源 JavaScript 库。该项目支持简体和繁体中文,提供超过 9000 个常用汉字的笔画数据,帮助开发者快速构建汉字学习应用。

项目简介

Hanzi Writer 是一个功能强大的 JavaScript 库,专门用于展示汉字笔画顺序动画和提供笔画练习测验。该项目由 chanind 开发维护,在 GitHub 上已获得 4.4k stars,使用 TypeScript 编写,确保了良好的类型安全和开发体验。

核心价值

  • 支持简体和繁体中文字符
  • 提供笔画顺序动画演示
  • 内置交互式笔画练习测验
  • 轻量级库(仅 35KB,gzip 后 10KB)
  • 可嵌入网页和移动应用

核心特性

  • 海量字符数据:包含超过 9000 个最常用的简体和繁体汉字
  • 笔画顺序动画:流畅的笔画绘制动画,可自定义速度和延迟
  • 交互式测验:支持用户手写输入,实时判断笔画正确性
  • 高度可定制:丰富的配置选项,支持自定义颜色、尺寸、动画速度等
  • 部首高亮:可以用不同颜色标记汉字的部首
  • 循环动画:支持连续循环播放笔画动画
  • 事件回调:提供完整的事件钩子,方便与应用集成
  • 轻量高效:库体积小,支持 SVG 和 Canvas 两种渲染方式
  • 跨平台支持:基于 HTML5 和 SVG,可在网页、iOS 和 Android 应用中使用

技术栈

  • TypeScript - 核心代码使用 TypeScript 编写
  • SVG / Canvas - 支持 SVG 和 Canvas 两种渲染模式
  • Rollup - 模块打包工具
  • Jest - 单元测试框架
  • Make Me a Hanzi - 笔画数据来源

安装指南

前置要求

  • 现代浏览器支持(Chrome、Firefox、Safari、Edge)
  • Node.js >= 14(如果使用 npm 安装)

通过 CDN 安装

最简单的方式是直接从 CDN 加载:

html
<script src="https://cdn.jsdelivr.net/npm/hanzi-writer@3.5/dist/hanzi-writer.min.js"></script>

加载后会创建全局变量 HanziWriter

通过 npm 安装

在项目中使用:

bash
npm install hanzi-writer

或使用 yarn:

bash
yarn add hanzi-writer

然后在代码中引入:

javascript
const HanziWriter = require('hanzi-writer');
// 或使用 ES6 模块
import HanziWriter from 'hanzi-writer';

快速开始

基础渲染

在 HTML 中创建目标容器:

html
<div id="character-target"></div>

在 JavaScript 中创建 HanziWriter 实例:

javascript
const writer = HanziWriter.create('character-target', '我', {
  width: 100,
  height: 100,
  padding: 5
});

笔画动画

javascript
const writer = HanziWriter.create('character-target', '国', {
  width: 100,
  height: 100,
  padding: 5,
  showOutline: true
});

// 点击按钮播放动画
document.getElementById('animate-btn').addEventListener('click', () => {
  writer.animateCharacter();
});

循环动画

javascript
const writer = HanziWriter.create('character-target', '轮', {
  width: 100,
  height: 100,
  padding: 5,
  delayBetweenLoops: 3000  // 每次循环间隔 3 秒
});

writer.loopCharacterAnimation();

使用示例

场景 1:部首高亮显示

javascript
const writer = HanziWriter.create('character-target', '草', {
  width: 150,
  height: 150,
  padding: 5,
  strokeColor: '#555',
  radicalColor: '#168F16'  // 部首用绿色显示
});

场景 2:自定义动画速度

javascript
const writer = HanziWriter.create('character-target', '激', {
  width: 100,
  height: 100,
  padding: 5,
  showOutline: false,
  strokeAnimationSpeed: 5,      // 5 倍速播放
  delayBetweenStrokes: 10,      // 笔画间隔 10ms
  radicalColor: '#337ab7'
});

writer.animateCharacter();

场景 3:交互式笔画测验

javascript
const writer = HanziWriter.create('character-target', '测', {
  width: 150,
  height: 150,
  showCharacter: false,  // 隐藏字符轮廓
  padding: 5
});

writer.quiz({
  onMistake: (strokeData) => {
    console.log('错误!当前笔画:', strokeData.strokeNum);
    console.log('此笔画错误次数:', strokeData.mistakesOnStroke);
    console.log('总错误次数:', strokeData.totalMistakes);
  },
  onCorrectStroke: (strokeData) => {
    console.log('正确!笔画 ' + strokeData.strokeNum);
    console.log('剩余笔画:', strokeData.strokesRemaining);
  },
  onComplete: (summaryData) => {
    console.log('完成!字符:', summaryData.character);
    console.log('总错误次数:', summaryData.totalMistakes);
  }
});

场景 4:链式动画

javascript
const char1 = HanziWriter.create('target-1', '很', {
  width: 100,
  height: 100,
  padding: 5,
  showCharacter: false
});

const char2 = HanziWriter.create('target-2', '爽', {
  width: 100,
  height: 100,
  padding: 5,
  showCharacter: false
});

function chainAnimations() {
  char1.hideCharacter();
  char2.hideCharacter();

  char1.animateCharacter({
    onComplete: () => {
      setTimeout(() => {
        char2.animateCharacter();
      }, 1000);  // 延迟 1 秒后播放第二个字
    }
  });
}

场景 5:自定义字符数据加载

javascript
const writer = HanziWriter.create('target', '我', {
  charDataLoader: (char, onComplete) => {
    // 使用自定义数据源
    fetch(`/my-server/${char}.json`)
      .then(res => res.json())
      .then(charData => onComplete(charData));
  }
});

或使用 hanzi-writer-data npm 包:

javascript
const renData = require('hanzi-writer-data/人');

const writer = HanziWriter.create('target', '人', {
  charDataLoader: () => renData
});

场景 6:原始 SVG 渲染

加载字符数据并自定义渲染:

javascript
HanziWriter.loadCharacterData('六').then(charData => {
  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  svg.style.width = '150px';
  svg.style.height = '150px';

  const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
  const transformData = HanziWriter.getScalingTransform(150, 150);
  group.setAttributeNS(null, 'transform', transformData.transform);
  svg.appendChild(group);

  charData.strokes.forEach(strokePath => {
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttributeNS(null, 'd', strokePath);
    path.style.fill = '#555';
    group.appendChild(path);
  });

  document.getElementById('target').appendChild(svg);
});

API 参考

创建实例

javascript
HanziWriter.create(element, character, options)

参数

  • element - DOM 元素或元素 ID
  • character - 要绘制的汉字,如 '你'
  • options - 配置对象

常用配置选项

选项类型默认值说明
widthnumber-画布宽度(px)
heightnumber-画布高度(px)
paddingnumber20字符与边缘的间距(px)
showOutlinebooleantrue是否显示字符轮廓
showCharacterbooleantrue是否显示字符
strokeColorstring'#555'笔画颜色
radicalColorstringnull部首颜色
highlightColorstring'#AAF'测验中的高亮颜色
outlineColorstring'#DDD'轮廓颜色
drawingColorstring'#333'用户绘制的颜色
strokeAnimationSpeednumber1动画速度(> 0)
delayBetweenStrokesnumber1000笔画间隔(ms)
delayBetweenLoopsnumber2000循环间隔(ms)
showHintAfterMissesnumber3多少次错误后显示提示

实例方法

显示/隐藏控制

  • writer.showCharacter(options) - 显示字符
  • writer.hideCharacter(options) - 隐藏字符
  • writer.showOutline(options) - 显示轮廓
  • writer.hideOutline(options) - 隐藏轮廓

动画控制

  • writer.animateCharacter(options) - 播放完整笔画动画
  • writer.animateStroke(strokeNum, options) - 播放单个笔画
  • writer.highlightStroke(strokeNum, options) - 高亮单个笔画
  • writer.loopCharacterAnimation() - 循环播放动画
  • writer.pauseAnimation() - 暂停动画
  • writer.resumeAnimation() - 恢复动画

测验控制

  • writer.quiz(options) - 开始测验
  • writer.cancelQuiz() - 取消测验

其他方法

  • writer.setCharacter(newChar) - 更换字符
  • writer.updateColor(colorName, colorValue) - 更新颜色
  • writer.updateDimensions(options) - 更新尺寸

静态方法

javascript
// 加载字符数据
HanziWriter.loadCharacterData(character, options)

// 获取缩放变换信息
HanziWriter.getScalingTransform(width, height, padding)

数据来源

Hanzi Writer 的笔画数据来自 Make Me a Hanzi 项目,该项目从 Arphic Technology(文鼎科技)的字体中提取数据。所有字符数据可在 Hanzi Writer Data 仓库中找到。

应用场景

  • 汉语学习应用:为外国学生提供笔画顺序学习
  • 儿童教育软件:帮助孩子学习汉字书写
  • 在线词典:展示汉字笔画顺序
  • 移动应用:嵌入 iOS/Android 应用的汉字学习模块
  • 教学网站:构建交互式汉字练习系统

浏览器兼容性

  • Chrome、Firefox、Safari、Edge(现代版本)
  • Internet Explorer 10/11(需要 Promise polyfill)

IE 兼容性配置

html
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hanzi-writer@3.5/dist/hanzi-writer.min.js"></script>

项目链接

许可证

  • Hanzi Writer 本身使用 MIT 许可证
  • 字符数据使用 Arphic Public License(文鼎公众授权)

贡献指南

欢迎提交 Pull Request!本地开发流程:

bash
# 克隆仓库
git clone https://github.com/chanind/hanzi-writer.git
cd hanzi-writer

# 安装依赖
yarn install

# 运行测试
yarn test

# 构建项目
yarn build

总结

Hanzi Writer 是一个功能完善、易于使用的汉字笔画动画库,非常适合用于构建汉语学习应用。它提供了丰富的 API 和灵活的配置选项,支持从简单的笔画演示到复杂的交互式测验等多种使用场景。凭借轻量级的体积和良好的跨平台支持,Hanzi Writer 是开发汉字学习工具的理想选择。

分享: