217 lines
6.1 KiB
TypeScript
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;
|