Frontend Development 20 min read

Generating Chinese Character Stroke Order Animations from Font Files

This article details a technical approach to automatically generating stroke order animation data for Chinese characters from standard font files, covering SVG rendering, coordinate transformations, deep learning-based stroke decomposition, and CSS animation techniques.

ByteFE
ByteFE
ByteFE
Generating Chinese Character Stroke Order Animations from Font Files

This article introduces a backend system designed to automatically generate stroke order animation data for Chinese characters from standard font files (WOFF, OTF, TTF). The system outputs JSON resources distributed via CDN, featuring manual intervention tools for stroke decomposition, direction adjustment, and scaling.

Rendering characters requires aligning the font coordinate system with SVG. Since TTF fonts use a baseline coordinate system while SVG uses a top-left origin, a transformation is applied: transform="scale(1, -1) translate(0, -900)" . This flips the Y-axis and shifts the glyph to the correct baseline position.

Animation effects are created by calculating the length of each stroke's median line and applying CSS keyframes. The stroke-dashoffset and stroke-dasharray properties simulate a drawing brush, while clip-path restricts the animation to the glyph's outline:

const animationStyle = `@keyframes ${keyframeId} {
    0% { stroke: blue; stroke-dashoffset: ${animation.offset}; stroke-width: ${animation.width}; }
    ${animation.fraction}% { stroke: blue; stroke-dashoffset: 0; stroke-width: ${animation.width}; }
    100% { stroke: var(--color-text-1); stroke-width: ${STANDARD_LEN}; }
  }
  #${animationId} { animation: ${keyframeId} ${duration}s linear ${delay}s both; }
  `;

Stroke decomposition begins by parsing TTF files with opentype.js to extract contour points. Corner points with tangent angle differences exceeding 18° are identified as potential stroke boundaries. A neural network ( convnetjs ) evaluates feature vectors between corners to generate matching scores:

const getFeatures = (ins, out) => {
  const diff = out.subtract(ins);
  const angle = Math.atan2(diff[1], diff[0]);
  const distance = Math.sqrt(out.distance2(ins));
  return [subtractAngle(angle, ins.angles[0]), subtractAngle(out.angles[1], angle), /* ... */];
};

The Hungarian algorithm solves the maximum weight bipartite matching problem to connect corners with "bridges," effectively splitting the glyph into individual strokes. Straight bridge lines are then smoothed using cubic Bezier curves to preserve natural brush tips.

Finally, stroke order is determined by generating Voronoi diagrams from sampled stroke points to extract median lines. These medians are matched against a structural tree derived from Ideographic Description Characters (IDC). The system produces compact ~4 kB JSON files, though complex characters still require manual verification. Future work aims to integrate error-correction algorithms to minimize human intervention.

Algorithmfrontend developmentSVG Animationcomputer graphicsFont ParsingStroke Order
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.