Files
doodle-mini/miniprogram/focusPages/shared/service/shapeSymbolDraw.ts
T
2026-04-09 17:43:22 +08:00

266 lines
8.5 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 '../../../core/draw/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.gridData = gridData;
this.prepareDraw();
await this.drawHeaderAndDivider();
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 legendStartX =
margin + (canvasWidth - margin * 2 - gridWidth) / 2; // 居中
const legendStartY = startY;
// 绘制参考网格的网格线
this.drawGridLines(
legendStartX,
legendStartY,
cellSize,
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;
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;
const symbolY = legendStartY + cellSize + cellSize / 2;
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;
// 计算练习区域起始X位置(居中)
const practiceStartX =
margin + (canvasWidth - margin * 2 - practiceGridWidth) / 2;
// 绘制练习区域网格线(使用 drawGridLines
this.drawGridLines(
practiceStartX,
practiceStartY,
practiceCellSize,
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,
);
}
// 第二行(符号行)留空,让小朋友涂符号
}
}
}
/**
* 绘制符号(使用路径绘制,类似 shapeSymbolShapes.ts
*/
private drawSymbol(
ctx: RenderingContext,
x: number,
y: number,
symbol: string,
size: number,
): void {
ctx.save();
ctx.translate(x, y);
const lineWidth = size * 0.15; // 线条宽度
const halfSize = size / 2;
const strokeLength = halfSize * 0.7; // 线条长度
ctx.strokeStyle = '#000';
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
switch (symbol) {
case '+':
// 加号:横线和竖线
ctx.beginPath();
// 横线
ctx.moveTo(-strokeLength, 0);
ctx.lineTo(strokeLength, 0);
// 竖线
ctx.moveTo(0, -strokeLength);
ctx.lineTo(0, strokeLength);
ctx.stroke();
break;
case '-':
// 减号:横线
ctx.beginPath();
ctx.moveTo(-strokeLength, 0);
ctx.lineTo(strokeLength, 0);
ctx.stroke();
break;
case '×':
// 乘号:两条斜线
ctx.beginPath();
// 左上到右下
ctx.moveTo(-strokeLength * 0.7, -strokeLength * 0.7);
ctx.lineTo(strokeLength * 0.7, strokeLength * 0.7);
// 右上到左下
ctx.moveTo(strokeLength * 0.7, -strokeLength * 0.7);
ctx.lineTo(-strokeLength * 0.7, strokeLength * 0.7);
ctx.stroke();
break;
case '✓':
// 对号:勾,整体更大,右侧的线更长
const checkScale = 1.2; // 对号整体放大1.2倍
ctx.beginPath();
const checkStartX = -strokeLength * 0.5 * checkScale;
const checkStartY = -strokeLength * 0.2 * checkScale;
const checkMidX = -strokeLength * 0.1 * checkScale;
const checkMidY = strokeLength * 0.3 * checkScale;
const checkEndX = strokeLength * 1.0 * checkScale; // 增加右侧长度
const checkEndY = -strokeLength * 0.4 * checkScale; // 稍微向上调整
ctx.moveTo(checkStartX, checkStartY);
ctx.lineTo(checkMidX, checkMidY);
ctx.lineTo(checkEndX, checkEndY);
ctx.stroke();
break;
default:
// 默认绘制加号
ctx.beginPath();
ctx.moveTo(-strokeLength, 0);
ctx.lineTo(strokeLength, 0);
ctx.moveTo(0, -strokeLength);
ctx.lineTo(0, strokeLength);
ctx.stroke();
}
ctx.restore();
}
}
export default ShapeSymbolDraw;