/** * 图形符号配对绘制服务 */ 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; } /** * 图形符号配对绘制服务 */ class ShapeSymbolDraw extends BaseDrawService { gridData: ShapeSymbolData | null = null; constructor( canvas: Canvas, ctx: RenderingContext, options?: Record, ) { 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 = { '+': '➕', '-': '➖', '×': '✖️', '✓': '✔️', }; 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;