Files
doodle-mini/miniprogram/focusPages/shared/service/shapeSymbolDraw.ts
T
2025-12-17 10:45:09 +08:00

266 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 图形符号配对绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { drawShapeSymbolShape } from '../shapes/shapeSymbolShapes';
/**
* 图形符号配对数据
*/
export interface ShapeSymbolData {
/** 图例映射(4个图形和符号的对应关系) */
legendMapping: Array<{
shapeId: string;
symbol: string;
color: string;
}>;
/** 练习区域的行数据(6行,每两行一组) */
practiceRows: Array<
Array<{ shapeId: string | null; symbol: string | null }>
>;
/** 图形颜色映射 */
shapeColorMap: Record<string, string>;
}
/**
* 图形符号配对绘制服务
*/
class ShapeSymbolDraw extends BaseDrawService {
gridData: ShapeSymbolData | null = null;
constructor(
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) {
super(canvas, ctx, options);
}
/**
* 绘制图形符号配对内容
*/
async draw(gridData: ShapeSymbolData) {
if (!gridData || !gridData.legendMapping || !gridData.practiceRows) {
return;
}
this.setPrintConfig();
this.gridData = gridData;
this.clear();
this.setPaper();
// 绘制Header
if (this.headerType !== 'minimal') {
await this.drawHeader();
} else {
this.drawMiniHeader();
}
// 绘制内容区域
this.drawDivider();
this.drawContent({
ctx: this.ctx,
gridData: this.gridData,
canvasWidth: this.canvasWidth,
startY: this.currentY,
});
}
/**
* 绘制内容区域
*/
private drawContent(params: {
ctx: RenderingContext;
gridData: ShapeSymbolData;
canvasWidth: number;
startY: number;
}): void {
const { ctx, gridData, canvasWidth } = params;
let { startY } = params;
startY = startY + 25;
const margin = 40; // 左右边距
const legendCols = 4; // 图例列数
const legendRows = 2; // 图例行数
const cellSize = 65; // 每个格子的大小
const gridWidth = cellSize * legendCols;
const gridHeight = cellSize * legendRows;
// 绘制参考网格(上面的九宫格)
const legendStartX =
margin + (canvasWidth - margin * 2 - gridWidth) / 2; // 居中
const legendStartY = startY;
// 绘制参考网格的网格线
this.drawGridLines(
ctx,
legendStartX,
legendStartY,
gridWidth,
gridHeight,
cellSize,
legendCols,
legendRows,
);
// 绘制图例内容
// 优化常量设置,减少重复计算,增强可维护性
// const legendColSpacing = 24; // 列间距(px),如需调整只此一处
// const legendCellHeight = cellSize; // 图例格子的高度,当前与cellSize一致
gridData.legendMapping.forEach((mapping, index: number) => {
if (!mapping) return;
const shapeX = legendStartX + index * cellSize + cellSize / 2; // 图片居中,假设图片大小50x50
const shapeY = legendStartY + cellSize / 2;
const shapeSize = 42;
const shapeColor = mapping.color || '#000';
drawShapeSymbolShape(
ctx,
mapping.shapeId,
shapeX,
shapeY,
shapeSize,
shapeColor,
);
// 第二行:绘制符号(使用 emoji)
const symbolX = legendStartX + index * cellSize + cellSize / 2; // 图片居中,假设图片大小50x50
const symbolY = legendStartY + cellSize + cellSize / 2 + 5;
this.drawSymbol(ctx, symbolX, symbolY, mapping.symbol, 30);
});
const dividerY = legendStartY + legendRows * cellSize + 30;
this.drawDashedDivider(dividerY, margin);
// 绘制练习区域(六行七列,使用网格线,确保正方形格子)
const practiceStartY = dividerY + 30;
const practiceCols = 8;
const practiceRows = 8;
const practiceCellSize = 60; // 练习格子大小(正方形)
const practiceGridWidth = practiceCellSize * practiceCols;
const practiceGridHeight = practiceCellSize * practiceRows;
// 计算练习区域起始X位置(居中)
const practiceStartX =
margin + (canvasWidth - margin * 2 - practiceGridWidth) / 2;
// 绘制练习区域网格线(使用 drawGridLines
this.drawGridLines(
ctx,
practiceStartX,
practiceStartY,
practiceGridWidth,
practiceGridHeight,
practiceCellSize,
practiceCols,
practiceRows,
);
// 绘制练习内容
for (let row = 0; row < practiceRows; row++) {
const practiceRow = gridData.practiceRows[row];
if (!practiceRow) continue;
const rowY = practiceStartY + row * practiceCellSize;
const cellCenterY = rowY + practiceCellSize / 2;
for (let col = 0; col < practiceCols; col++) {
const cell = practiceRow[col];
if (!cell) continue;
const colX = practiceStartX + col * practiceCellSize;
const cellCenterX = colX + practiceCellSize / 2;
// 如果第一行(图形行),绘制图形(使用相同颜色,无边框,居中)
if (row % 2 === 0 && cell.shapeId) {
const shapeSize = 42;
const shapeColor =
gridData.shapeColorMap[cell.shapeId] || '#000';
drawShapeSymbolShape(
ctx,
cell.shapeId,
cellCenterX,
cellCenterY,
shapeSize,
shapeColor,
);
}
// 第二行(符号行)留空,让小朋友涂符号
}
}
}
/**
* 绘制符号(使用 emoji 文字)
*/
private drawSymbol(
ctx: RenderingContext,
x: number,
y: number,
symbol: string,
fontSize: number,
): void {
ctx.save();
// 符号映射:将符号转换为 emoji
const symbolMap: Record<string, string> = {
'+': '',
'-': '',
'×': '✖️',
'✓': '✔️',
};
const emojiSymbol = symbolMap[symbol] || symbol;
ctx.font = `bold ${fontSize}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = '#000';
ctx.fillText(emojiSymbol, x, y);
ctx.restore();
}
/**
* 绘制网格线
*/
private drawGridLines(
ctx: RenderingContext,
gridStartX: number,
gridStartY: number,
gridWidth: number,
gridHeight: number,
cellSize: number,
legendCols: number,
legendRows: number,
): void {
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.setLineDash([]); // 实线
// 绘制垂直线
for (let i = 0; i <= legendCols; i++) {
const x = gridStartX + i * cellSize;
ctx.beginPath();
ctx.moveTo(x, gridStartY);
ctx.lineTo(x, gridStartY + gridHeight);
ctx.stroke();
}
// 绘制水平线
for (let i = 0; i <= legendRows; i++) {
const y = gridStartY + i * cellSize;
ctx.beginPath();
ctx.moveTo(gridStartX, y);
ctx.lineTo(gridStartX + gridWidth, y);
ctx.stroke();
}
}
}
export default ShapeSymbolDraw;