Animating Chinese Character Transformations with D3.js: From SVG Paths to FIFO Queue Animation
This article demonstrates how to convert Chinese characters into SVG paths using Batik, then creates a D3.js animation that morphs one character into another, addressing path length mismatches with a FIFO‑style point‑by‑point transition and adding a swing effect via setInterval.
One month ago I wrote an article about using Batik in Kotlin to convert TTF fonts to SVG images, extracting the SVG Path of Chinese characters. Building on that, I explore animating a transformation from one character to another using D3.js.
I chose the font "Aa剑豪体" and randomly selected the characters 鼠 (mouse) and 鸭 (duck). Using the method described earlier, I extracted their complete SVG shapes.
With the two SVG paths ready, I created a simple D3.js animation that swaps the path data, adjusting the SVG size, zoom, and transform properties for better visual fit. The initial HTML skeleton looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>鼠鼠我鸭</title>
</head>
<body style="text-align: center">
<script src="https://d3js.org/d3.v7.min.js"></script>
<script type="module">
const _svg = d3.select("body")
.append("svg")
.attr("width", "1000")
.attr("height", "1000")
.style("zoom", "0.3")
.style("transform", "rotateX(180deg)");
// ... (animation code)
</script>
</body>
</html>The animation initially looks chaotic because the two paths have very different lengths and command sequences, causing abrupt transitions. Using d3-interpolate-path with D3 v5 did not solve the issue, and the interpolation sometimes produced NaN values.
To achieve a smoother point‑by‑point morph, I adopted a FIFO (first‑in‑first‑out) approach: the strokes of 鼠 gradually disappear while the strokes of 鸭 are drawn sequentially.
The first step is to split the target path into individual command segments. The following JavaScript extracts the segments by removing the leading M and trailing Z and using a regular expression to capture each command:
const source = 鼠的SVG_Path(没有MZ);
const result = 鸭的SVG_Path(没有MZ);
const actionReg = new RegExp(/[a-z]/, "gi");
const data = new Array();
let match;
let lastIndex;
while ((match = actionReg.exec(result))) {
data.push(result.substring(lastIndex, match.index));
lastIndex = match.index;
}
data.push(result.substring(lastIndex));After splitting, I verified the segments by incrementally appending them to a red path element, which makes the intermediate steps easy to observe.
let tran = g.append("path")
.attr("fill", "red")
.attr("stroke", "black")
.attr("stroke-width", "4")
.attr("d", "M" + source + "Z")
.transition()
.delay(800);
let step = "L";
data.map(item => {
step += item + " ";
tran = tran.transition().attr("d", "M" + source + step.trimEnd() + "Z").duration(20);
});Having confirmed the incremental drawing, I moved to the FIFO animation. Because SVG transitions alone cannot handle the complex sequencing, I stored each frame as a function in an array and executed them with setInterval:
let path = g.append("path")
.attr("fill", "red")
.attr("d", "M" + source + "Z");
const funs = [];
let pre = source;
let step = "";
data.map((item, i) => {
step += item + " ";
const match = pre && actionReg.exec(source);
if (!match) {
pre = "";
} else if (["M", "L", "T"].indexOf(match[0]) !== -1) {
pre = source.substring(match.index + 1);
}
const d = "M" + pre + (pre ? "L" : "") + step.trimEnd() + "Z";
funs.push(() => path.attr("d", d));
});
const animation = setInterval(() => {
if (!funs.length) { clearInterval(animation); return; }
funs.shift()();
}, 20);To make the animation more lively, I added a swing effect by repeatedly applying a skew transform to the path:
let pathTran = path;
Array(8).fill(0).forEach(() => {
pathTran = pathTran.transition()
.attr("transform", "skewX(10)")
.duration(300)
.transition()
.attr("transform", "skewX(-10)")
.duration(300);
});
pathTran.transition().attr("transform", "").duration(600);The final result is a concise yet expressive animation that morphs one Chinese character into another using a FIFO‑style point transition, complemented by a subtle swinging motion. The approach showcases how D3.js can be combined with plain JavaScript timers to achieve custom SVG animations beyond built‑in transition capabilities.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
