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

217 lines
6.1 KiB
TypeScript

/**
* 计算练习题绘制服务
*/
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<string, any>,
) {
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<void> {
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;