/** * 计算练习题绘制服务 */ import { BaseDrawService } from '../../../core/draw/baseDraw'; /** * 计算练习题数据 */ export interface CalculationPracticeData { problems: Array<{ left: number; operator: '+' | '-'; right: number; result: number; }>; } /** * 计算练习题绘制服务 */ class CalculationPracticeDraw extends BaseDrawService { practiceData: CalculationPracticeData | null = null; constructor( canvas: Canvas, ctx: RenderingContext, options?: Record, ) { super(canvas, ctx, options); this.practiceData = null; } /** * 绘制计算练习题内容 */ async draw(practiceData: CalculationPracticeData) { if (!practiceData || !practiceData.problems) { return; } this.practiceData = practiceData; this.prepareDraw(); await this.drawHeaderAndDivider(); await this.drawContent({ canvas: this.canvas, ctx: this.ctx, problems: this.practiceData.problems, canvasWidth: this.canvasWidth, startY: this.currentY, }); } /** * 绘制内容区域 */ private async drawContent(params: { canvas: WechatMiniprogram.Canvas; ctx: RenderingContext; problems: Array<{ left: number; operator: '+' | '-'; right: number; result: number; }>; canvasWidth: number; startY: number; }): Promise { const { ctx, problems, canvasWidth, startY } = params; const rowStartY = startY + 30; // 顶部间距 const margin = 30; const rows = 12; const cols = 3; const rowSpacing = 60; // 行间距 const colSpacing = 15; // 列间距 // 计算可用宽度 const availableWidth = canvasWidth - margin * 2; const colWidth = (availableWidth - colSpacing * (cols - 1)) / cols; // 数字和符号相关参数 const fontSize = 18; const numberSpacing = 5; // 数字之间的间距 const boxSize = 28; // 答案框大小 const boxBorderColor = '#555'; // 答案框边框颜色 const symbolWidth = 16; // 符号宽度 // 计算每道题的起始位置(10行3列) for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { const problemIndex = row * cols + col; if (problemIndex >= problems.length) { break; } const problem = problems[problemIndex]; const problemX = margin + col * (colWidth + colSpacing); const problemY = rowStartY + row * rowSpacing; // 绘制单道题 this.drawSingleProblem( ctx, problemX, problemY, colWidth, problem, { fontSize, numberSpacing, boxSize, boxBorderColor, symbolWidth, }, ); } } } /** * 绘制单道题 */ private drawSingleProblem( ctx: RenderingContext, x: number, y: number, width: number, problem: { left: number; operator: '+' | '-'; right: number; result: number; }, options: { fontSize: number; numberSpacing: number; boxSize: number; boxBorderColor: string; symbolWidth: number; }, ) { const { fontSize, numberSpacing, boxSize, boxBorderColor, symbolWidth, } = options; // 计算每个元素的宽度(统一使用,确保对齐) const numberWidth = fontSize * 1.5; // 数字宽度(考虑两位数) // const elementWidth = Math.max(numberWidth, boxSize); const elementWidth = numberWidth; // 计算总宽度并居中 const totalWidth = elementWidth + numberSpacing + symbolWidth + numberSpacing + elementWidth + numberSpacing + symbolWidth + numberSpacing + elementWidth; const startX = x + (width - totalWidth) / 2; let currentX = startX; // 答案框的Y坐标 const resultBoxY = y; // 数字和符号的Y坐标(与答案框垂直居中对齐) const centerY = resultBoxY + boxSize / 2; // 绘制左侧数字 const leftCenterX = currentX + elementWidth / 2; ctx.fillStyle = '#000'; ctx.font = `${fontSize}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(String(problem.left), leftCenterX, centerY); currentX += elementWidth + numberSpacing; // 绘制运算符(与数字垂直居中对齐) const operatorX = currentX + symbolWidth / 2; this.drawSymbol( operatorX, centerY, problem.operator, symbolWidth * 0.8, ); currentX = operatorX + symbolWidth / 2 + numberSpacing; // 绘制右侧数字 const rightCenterX = currentX + elementWidth / 2; ctx.fillText(String(problem.right), rightCenterX, centerY); currentX += elementWidth + numberSpacing; // 绘制等号(与数字垂直居中对齐) const equalsX = currentX + symbolWidth / 2; this.drawSymbol(equalsX, centerY, '=', symbolWidth * 0.8); currentX = equalsX + symbolWidth / 2 + numberSpacing; // 绘制答案框 const resultBoxX = currentX + (elementWidth - boxSize) / 2; this.drawRoundedRect(resultBoxX, resultBoxY, boxSize, boxSize, { isDashed: false, radius: 4, color: boxBorderColor, lineWidth: 1, }); } } export default CalculationPracticeDraw;