feat:修改练习字帖生产逻辑

This commit is contained in:
R524809
2025-10-29 17:45:12 +08:00
parent 643a7e23e7
commit c4f27f2125
13 changed files with 141224 additions and 60061 deletions
+43 -21
View File
@@ -1,5 +1,6 @@
import { PAPER_SIZE } from '../constants/colors';
import { getMiniCodeImage, getImage } from '../utils/index';
import { CharacterItem } from '../types/characterType';
/**
* 仅支持 M/L/Z 的简单 SVG 路径解析与绘制
@@ -246,10 +247,9 @@ class WordDrawService {
}
/**
* 生成练字帖
* @param wordsMap 形如 { "其": ["M.. L.. Z", ...], ... },不超过 10 个汉字
* 绘制页眉和基础布局
*/
async draw(wordsMap: Record<string, string[]>) {
async drawLayout() {
this.setPrintConfig();
this.clear();
this.setPaper();
@@ -260,9 +260,35 @@ class WordDrawService {
} else {
await this.drawMiniHeader();
}
}
// 页眉绘制完成后,再绘制内容
this.drawContent(wordsMap);
async drawContentEmpty() {
this.clearContentArea();
this.drawContent(null);
}
/**
* 绘制田字格和练字内容
* @param wordsMap 形如 { "其": ["M.. L.. Z", ...], ... },不超过 10 个汉字
*/
drawPracticeContent(characters: CharacterItem[]) {
// 清空内容区域(页眉以下的部分)
this.clearContentArea();
// 绘制田字格和练字内容
this.drawContent(characters);
}
/**
* 清空内容区域(页眉以下的部分)
*/
private clearContentArea() {
const { ctx, canvas } = this;
const contentStartY = this.currentY;
// 清空页眉以下的所有内容
ctx.fillStyle = '#fff';
ctx.fillRect(0, contentStartY, canvas.width, canvas.height - contentStartY);
}
async drawHeader() {
@@ -353,10 +379,8 @@ class WordDrawService {
* rowGap 作用解释:
* rowGap(行间距)用于田字格竖直方向(每一行田字格之间的间隔)的初始最小值。它防止行与行的田字格上下贴得太紧,增加一定的可视呼吸空间。之后同样会结合剩余空间动态调整为实际间距 actualRowGap。
*/
drawContent(wordsMap: Record<string, string[]>) {
drawContent(characterData: CharacterItem[] | null) {
const { canvas, ctx } = this;
const characters = Object.keys(wordsMap).slice(0, 10);
// 布局参数
const topGap = 50; // 与页眉分割线的距离
@@ -398,11 +422,11 @@ class WordDrawService {
columnNumber
});
if (characters.length > 0) {
if (characterData && characterData.length > 0) {
// 第二阶段:绘制练字内容
this.drawPracticeContent({
this.drawPracticeContentInternal({
ctx,
wordsMap,
characterData,
startX: leftMargin,
startY: contentTop,
cellSize,
@@ -464,9 +488,9 @@ class WordDrawService {
/**
* 绘制练字内容(参考 template.html 的实现)
*/
drawPracticeContent({
drawPracticeContentInternal({
ctx,
wordsMap,
characterData,
startX,
startY,
cellSize,
@@ -475,7 +499,7 @@ class WordDrawService {
columnNumber
}: {
ctx: RenderingContext;
wordsMap: Record<string, string[]>;
characterData: CharacterItem[];
startX: number;
startY: number;
cellSize: number;
@@ -483,19 +507,17 @@ class WordDrawService {
rowGap: number;
columnNumber: number;
}) {
const characters = Object.keys(wordsMap);
const reservedCells = 2;
let rowIndex = 0, columnIndex = 0;
// console.log('开始绘制练字内容,汉字数量:', characters.length);
characters.forEach((uniqueKey, charIndex) => {
const strokes = wordsMap[uniqueKey] || [];
characterData.forEach((item, index) => {
const character = item.character;
const strokes = item.strokes;
const strokeCount = strokes.length;
// 从uniqueKey中提取原始汉字(去掉后缀)
const originalChar = uniqueKey.split('_')[0];
console.log(`绘制第 ${charIndex + 1} 个汉字 "${originalChar}",笔画数: ${strokeCount}`);
console.log(`绘制第 ${index + 1} 个汉字 "${character}",笔画数: ${strokeCount}`);
// 1. 预览格:显示完整汉字(黑色)
this.drawPreviewCell({
@@ -538,7 +560,7 @@ class WordDrawService {
}
// 4. 强制下一个汉字换行到新行的第一个田字格
if (charIndex < characters.length - 1) { // 不是最后一个汉字
if (index < characterData.length - 1) { // 不是最后一个汉字
rowIndex = rowIndex + 1;
columnIndex = 0;
// console.log(`汉字 "${char}" 绘制完成,强制换行到新行,下一个汉字从 rowIndex: ${rowIndex}, columnIndex: ${columnIndex} 开始`);