feat:练字贴生成
This commit is contained in:
@@ -116,7 +116,6 @@ function drawStrokes({
|
||||
}: DrawStrokesParams) {
|
||||
// 模板中使用 54x54 的视窗尺寸
|
||||
const viewBoxSize = 54;
|
||||
const scale = size / viewBoxSize;
|
||||
|
||||
// 添加内边距:在田字格四周预留空间,避免笔画贴边
|
||||
const padding = size * 0.1; // 内边距为田字格大小的10%
|
||||
@@ -127,22 +126,22 @@ function drawStrokes({
|
||||
const contentOffsetX = offsetX + padding;
|
||||
const contentOffsetY = offsetY + padding;
|
||||
|
||||
console.log('drawStrokes 参数:', {
|
||||
strokesCount: strokes.length,
|
||||
uptoInclusive,
|
||||
offsetX,
|
||||
offsetY,
|
||||
size,
|
||||
padding,
|
||||
contentSize,
|
||||
contentOffsetX,
|
||||
contentOffsetY,
|
||||
originalScale: scale,
|
||||
contentScale,
|
||||
fillStyle,
|
||||
strokeStyle,
|
||||
lineWidth
|
||||
});
|
||||
// console.log('drawStrokes 参数:', {
|
||||
// strokesCount: strokes.length,
|
||||
// uptoInclusive,
|
||||
// offsetX,
|
||||
// offsetY,
|
||||
// size,
|
||||
// padding,
|
||||
// contentSize,
|
||||
// contentOffsetX,
|
||||
// contentOffsetY,
|
||||
// originalScale: scale,
|
||||
// contentScale,
|
||||
// fillStyle,
|
||||
// strokeStyle,
|
||||
// lineWidth
|
||||
// });
|
||||
|
||||
for (let s = 0; s <= uptoInclusive && s < strokes.length; s++) {
|
||||
// console.log(`绘制第 ${s} 个笔画:`, strokes[s]);
|
||||
@@ -357,7 +356,7 @@ class WordDrawService {
|
||||
drawContent(wordsMap: Record<string, string[]>) {
|
||||
const { canvas, ctx } = this;
|
||||
const characters = Object.keys(wordsMap).slice(0, 10);
|
||||
if (characters.length === 0) return;
|
||||
|
||||
|
||||
// 布局参数
|
||||
const topGap = 50; // 与页眉分割线的距离
|
||||
@@ -365,7 +364,6 @@ class WordDrawService {
|
||||
const rightMargin = 120;
|
||||
const bottomMargin = 120;
|
||||
const contentTop = this.currentY + topGap;
|
||||
console.log('contentTop----:', contentTop)
|
||||
const contentWidth = canvas.width - leftMargin - rightMargin;
|
||||
const contentHeight = canvas.height - contentTop - bottomMargin;
|
||||
|
||||
@@ -374,24 +372,19 @@ class WordDrawService {
|
||||
const rowGap = 36; // 最小格子行间距,用于保证纵向不拥挤,后续可动态调整为实际间距
|
||||
|
||||
// 计算每行可容纳的田字格数量
|
||||
const maxPerRow = Math.max(1, Math.floor((contentWidth + minGap) / (cellSize + minGap)));
|
||||
const columnNumber = Math.max(1, Math.floor((contentWidth + minGap) / (cellSize + minGap)));
|
||||
|
||||
// 计算每列可容纳的田字格数量
|
||||
const actualRows = Math.max(1, Math.floor((contentHeight + rowGap) / (cellSize + rowGap)));
|
||||
const rowNumber = Math.max(1, Math.floor((contentHeight + rowGap) / (cellSize + rowGap)));
|
||||
|
||||
// 计算总需要的田字格数量
|
||||
const totalCellsNeeded = actualRows * maxPerRow;
|
||||
|
||||
// 计算实际需要的行数
|
||||
console.log('actualRows----:', actualRows)
|
||||
console.log('totalCellsNeeded----:', totalCellsNeeded)
|
||||
console.log('maxPerRow----:', maxPerRow)
|
||||
const totalCellsNeeded = rowNumber * columnNumber;
|
||||
|
||||
// 动态调整间距以充分利用空间(在允许的最小间距基础上弹性扩展以适配画布)
|
||||
const actualRowGap = actualRows > 1 ? (contentHeight - actualRows * cellSize) / (actualRows - 1) : 0;
|
||||
const actualColGap = maxPerRow > 1 ? (contentWidth - maxPerRow * cellSize) / (maxPerRow - 1) : 0;
|
||||
const actualRowGap = rowNumber > 1 ? (contentHeight - rowNumber * cellSize) / (rowNumber - 1) : 0;
|
||||
const actualColGap = columnNumber > 1 ? (contentWidth - columnNumber * cellSize) / (columnNumber - 1) : 0;
|
||||
|
||||
console.log(`布局信息: 总格数=${totalCellsNeeded}, 行数=${actualRows}, 列数=${maxPerRow}, 行间距=${actualRowGap.toFixed(1)}, 列间距=${actualColGap.toFixed(1)}`);
|
||||
console.log(`布局信息: 总格数=${totalCellsNeeded}, 行数=${rowNumber}, 列数=${columnNumber}, 行间距=${actualRowGap.toFixed(1)}, 列间距=${actualColGap.toFixed(1)}`);
|
||||
|
||||
// 第一阶段:绘制所有空田字格
|
||||
this.drawEmptyGrids({
|
||||
@@ -401,21 +394,23 @@ class WordDrawService {
|
||||
cellSize,
|
||||
colGap: actualColGap,
|
||||
rowGap: actualRowGap,
|
||||
maxPerRow,
|
||||
maxRows: actualRows
|
||||
rowNumber,
|
||||
columnNumber
|
||||
});
|
||||
|
||||
// 第二阶段:绘制练字内容
|
||||
this.drawPracticeContent({
|
||||
ctx,
|
||||
wordsMap,
|
||||
startX: leftMargin,
|
||||
startY: contentTop,
|
||||
cellSize,
|
||||
colGap: actualColGap,
|
||||
rowGap: actualRowGap,
|
||||
maxPerRow
|
||||
});
|
||||
if (characters.length > 0) {
|
||||
// 第二阶段:绘制练字内容
|
||||
this.drawPracticeContent({
|
||||
ctx,
|
||||
wordsMap,
|
||||
startX: leftMargin,
|
||||
startY: contentTop,
|
||||
cellSize,
|
||||
colGap: actualColGap,
|
||||
rowGap: actualRowGap,
|
||||
columnNumber
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,8 +439,8 @@ class WordDrawService {
|
||||
cellSize,
|
||||
colGap,
|
||||
rowGap,
|
||||
maxPerRow,
|
||||
maxRows
|
||||
rowNumber,
|
||||
columnNumber
|
||||
}: {
|
||||
ctx: RenderingContext;
|
||||
startX: number;
|
||||
@@ -453,12 +448,12 @@ class WordDrawService {
|
||||
cellSize: number;
|
||||
colGap: number;
|
||||
rowGap: number;
|
||||
maxPerRow: number;
|
||||
maxRows: number;
|
||||
rowNumber: number; // 行数
|
||||
columnNumber: number; // 列数
|
||||
}) {
|
||||
console.log('drawEmptyGrids startY----:', startY)
|
||||
for (let row = 0; row < maxRows; row++) {
|
||||
for (let col = 0; col < maxPerRow; col++) {
|
||||
for (let row = 0; row < rowNumber; row++) {
|
||||
for (let col = 0; col < columnNumber; col++) {
|
||||
const x = startX + col * (cellSize + colGap) + cellSize / 2;
|
||||
const y = startY + row * (cellSize + rowGap) + cellSize / 2;
|
||||
drawTianZiGrid({ ctx, x, y, size: cellSize });
|
||||
@@ -477,7 +472,7 @@ class WordDrawService {
|
||||
cellSize,
|
||||
colGap,
|
||||
rowGap,
|
||||
maxPerRow
|
||||
columnNumber
|
||||
}: {
|
||||
ctx: RenderingContext;
|
||||
wordsMap: Record<string, string[]>;
|
||||
@@ -486,18 +481,21 @@ class WordDrawService {
|
||||
cellSize: number;
|
||||
colGap: number;
|
||||
rowGap: number;
|
||||
maxPerRow: number;
|
||||
columnNumber: number;
|
||||
}) {
|
||||
const characters = Object.keys(wordsMap);
|
||||
let cellIndex = 0;
|
||||
const reservedCells = 2;
|
||||
let rowIndex = 0, columnIndex = 0;
|
||||
|
||||
console.log('开始绘制练字内容,汉字数量:', characters.length);
|
||||
// console.log('开始绘制练字内容,汉字数量:', characters.length);
|
||||
|
||||
characters.forEach((char, charIndex) => {
|
||||
const strokes = wordsMap[char] || [];
|
||||
characters.forEach((uniqueKey, charIndex) => {
|
||||
const strokes = wordsMap[uniqueKey] || [];
|
||||
const strokeCount = strokes.length;
|
||||
// 从uniqueKey中提取原始汉字(去掉后缀)
|
||||
const originalChar = uniqueKey.split('_')[0];
|
||||
|
||||
console.log(`绘制第 ${charIndex + 1} 个汉字 "${char}",笔画数: ${strokeCount}`);
|
||||
console.log(`绘制第 ${charIndex + 1} 个汉字 "${originalChar}",笔画数: ${strokeCount}`);
|
||||
|
||||
// 1. 预览格:显示完整汉字(黑色)
|
||||
this.drawPreviewCell({
|
||||
@@ -508,10 +506,9 @@ class WordDrawService {
|
||||
cellSize,
|
||||
colGap,
|
||||
rowGap,
|
||||
maxPerRow,
|
||||
cellIndex
|
||||
rowIndex,
|
||||
});
|
||||
cellIndex++;
|
||||
columnIndex++;
|
||||
|
||||
// 2. 练习格:逐笔画显示(红色)
|
||||
for (let strokeIndex = 0; strokeIndex < strokeCount; strokeIndex++) {
|
||||
@@ -525,29 +522,30 @@ class WordDrawService {
|
||||
cellSize,
|
||||
colGap,
|
||||
rowGap,
|
||||
maxPerRow,
|
||||
cellIndex
|
||||
rowIndex,
|
||||
columnIndex
|
||||
});
|
||||
cellIndex++;
|
||||
|
||||
console.log(`笔画 ${strokeIndex + 1} 绘制完成,当前 cellIndex: ${cellIndex}`);
|
||||
columnIndex++;
|
||||
if (columnIndex >= columnNumber) {
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
}
|
||||
// console.log(`笔画 ${strokeIndex + 1} 绘制完成,当前 columnIndex: ${columnIndex}`);
|
||||
}
|
||||
if (columnIndex + reservedCells > columnNumber) {
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
// 3. 汉字间留空:每个汉字后留2个空白格
|
||||
cellIndex += 0;
|
||||
|
||||
// 4. 强制下一个汉字换行到新行的第一个田字格
|
||||
if (charIndex < characters.length - 1) { // 不是最后一个汉字
|
||||
const currentRow = Math.floor(cellIndex / maxPerRow);
|
||||
const nextRow = currentRow + 1;
|
||||
cellIndex = nextRow * maxPerRow;
|
||||
console.log(`汉字 "${char}" 绘制完成,强制换行到新行,下一个汉字从 cellIndex: ${cellIndex} 开始`);
|
||||
} else {
|
||||
console.log(`汉字 "${char}" 绘制完成,这是最后一个汉字`);
|
||||
rowIndex = rowIndex + 1;
|
||||
columnIndex = 0;
|
||||
// console.log(`汉字 "${char}" 绘制完成,强制换行到新行,下一个汉字从 rowIndex: ${rowIndex}, columnIndex: ${columnIndex} 开始`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('所有汉字绘制完成');
|
||||
// console.log('所有汉字绘制完成');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,8 +559,7 @@ class WordDrawService {
|
||||
cellSize,
|
||||
colGap,
|
||||
rowGap,
|
||||
maxPerRow,
|
||||
cellIndex
|
||||
rowIndex
|
||||
}: {
|
||||
ctx: RenderingContext;
|
||||
strokes: string[];
|
||||
@@ -571,15 +568,13 @@ class WordDrawService {
|
||||
cellSize: number;
|
||||
colGap: number;
|
||||
rowGap: number;
|
||||
maxPerRow: number;
|
||||
cellIndex: number;
|
||||
rowIndex: number;
|
||||
}) {
|
||||
const row = Math.floor(cellIndex / maxPerRow);
|
||||
const col = cellIndex % maxPerRow;
|
||||
const x = startX + col * (cellSize + colGap);
|
||||
const y = startY + row * (cellSize + rowGap);
|
||||
const columnIndex = 0;
|
||||
const x = startX + columnIndex * (cellSize + colGap);
|
||||
const y = startY + rowIndex * (cellSize + rowGap);
|
||||
|
||||
console.log(`预览格 [${cellIndex}] 坐标: (${x}, ${y}), 行列: (${row}, ${col}), 笔画数: ${strokes.length}`);
|
||||
// console.log(`预览格 [${rowIndex}, ${columnIndex}] 坐标: (${x}, ${y}), 笔画数: ${strokes.length}`);
|
||||
|
||||
// 绘制完整汉字(黑色,较粗)
|
||||
drawStrokes({
|
||||
@@ -607,8 +602,8 @@ class WordDrawService {
|
||||
cellSize,
|
||||
colGap,
|
||||
rowGap,
|
||||
maxPerRow,
|
||||
cellIndex
|
||||
rowIndex,
|
||||
columnIndex
|
||||
}: {
|
||||
ctx: RenderingContext;
|
||||
strokes: string[];
|
||||
@@ -618,15 +613,13 @@ class WordDrawService {
|
||||
cellSize: number;
|
||||
colGap: number;
|
||||
rowGap: number;
|
||||
maxPerRow: number;
|
||||
cellIndex: number;
|
||||
rowIndex: number;
|
||||
columnIndex: number;
|
||||
}) {
|
||||
const row = Math.floor(cellIndex / maxPerRow);
|
||||
const col = cellIndex % maxPerRow;
|
||||
const x = startX + col * (cellSize + colGap);
|
||||
const y = startY + row * (cellSize + rowGap);
|
||||
const x = startX + columnIndex * (cellSize + colGap);
|
||||
const y = startY + rowIndex * (cellSize + rowGap);
|
||||
|
||||
console.log(`练习格 [${cellIndex}] 坐标: (${x}, ${y}), 行列: (${row}, ${col}), 笔画: ${strokeIndex + 1}/${strokes.length}`);
|
||||
// console.log(`练习格 [${rowIndex}, ${columnIndex}] 坐标: (${x}, ${y}), 笔画: ${strokeIndex + 1}/${strokes.length}`);
|
||||
|
||||
// 绘制到指定笔画的汉字(红色,中等粗细)
|
||||
drawStrokes({
|
||||
@@ -643,7 +636,6 @@ class WordDrawService {
|
||||
}
|
||||
|
||||
drawLine(linY: number) {
|
||||
console.log('drawLine linY----:', linY)
|
||||
const { canvas, ctx } = this;
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
Reference in New Issue
Block a user