feat:V2.5.5方格推理
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* 方格推理绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
|
||||
/**
|
||||
* 3x3网格中的位置坐标
|
||||
*/
|
||||
export interface GridPosition {
|
||||
x: number; // 0-2
|
||||
y: number; // 0-2
|
||||
}
|
||||
|
||||
/**
|
||||
* 方格推理数据
|
||||
*/
|
||||
export interface GridReasoningData {
|
||||
/** 每行的数据 */
|
||||
rows: Array<{
|
||||
/** 第一个方格涂色位置 */
|
||||
grid1: GridPosition[];
|
||||
/** 第二个方格涂色位置 */
|
||||
grid2: GridPosition[];
|
||||
/** 运算符:+ 或 - */
|
||||
operator: '+' | '-';
|
||||
/** 结果方格涂色位置 */
|
||||
result: GridPosition[];
|
||||
}>;
|
||||
/** 右侧答案选项(随机排列的方格) */
|
||||
answerOptions: Array<{
|
||||
/** 方格涂色位置 */
|
||||
positions: GridPosition[];
|
||||
/** 对应的行索引(用于匹配答案) */
|
||||
rowIndex: number;
|
||||
}>;
|
||||
/** 使用的颜色 */
|
||||
color: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 方格推理绘制服务
|
||||
*/
|
||||
class GridReasoningDraw extends BaseDrawService {
|
||||
gridData: GridReasoningData | null = null;
|
||||
|
||||
constructor(
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) {
|
||||
super(canvas, ctx, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制方格推理内容
|
||||
*/
|
||||
async draw(gridData: GridReasoningData) {
|
||||
if (!gridData || !gridData.rows || !gridData.answerOptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setPrintConfig();
|
||||
this.gridData = gridData;
|
||||
this.clear();
|
||||
this.setPaper();
|
||||
|
||||
// 绘制Header
|
||||
if (this.headerType !== 'minimal') {
|
||||
await this.drawHeader();
|
||||
} else {
|
||||
this.drawMiniHeader();
|
||||
}
|
||||
|
||||
// 绘制内容区域
|
||||
this.drawDivider();
|
||||
await this.drawContent({
|
||||
canvas: this.canvas,
|
||||
ctx: this.ctx,
|
||||
gridData: this.gridData,
|
||||
canvasWidth: this.canvasWidth,
|
||||
startY: this.currentY,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制内容区域
|
||||
*/
|
||||
private async drawContent(params: {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
gridData: GridReasoningData;
|
||||
canvasWidth: number;
|
||||
startY: number;
|
||||
}): Promise<void> {
|
||||
const { ctx, gridData, canvasWidth } = params;
|
||||
let { startY } = params;
|
||||
|
||||
startY = startY + 30;
|
||||
|
||||
const margin = 40; // 左右边距
|
||||
const gridSize = 3; // 3x3网格
|
||||
const cellSize = 32; // 每个格子的宽度
|
||||
const gridWidth = cellSize * gridSize;
|
||||
const gridHeight = cellSize * gridSize;
|
||||
|
||||
// 左侧区域宽度(两个网格 + 运算符 + 间距)
|
||||
const gridSpacing = 15; // 两个网格之间的间距
|
||||
const operatorWidth = 40; // 运算符宽度
|
||||
const dotRadius = 7; // 圆点半径
|
||||
const dotSpacing = 20; // 圆点与网格的间距
|
||||
|
||||
// 右侧区域宽度(答案选项网格)
|
||||
const rightAreaWidth = gridWidth;
|
||||
const rightAreaStartX = canvasWidth - margin - rightAreaWidth;
|
||||
|
||||
// 行间距
|
||||
const rowSpacing = 38;
|
||||
|
||||
// 打乱答案选项的顺序,实现随机乱序
|
||||
const shuffledAnswers = [...gridData.answerOptions].sort(
|
||||
() => Math.random() - 0.5,
|
||||
);
|
||||
|
||||
// 绘制5行
|
||||
for (let rowIndex = 0; rowIndex < 5; rowIndex++) {
|
||||
const rowData = gridData.rows[rowIndex];
|
||||
if (!rowData) continue;
|
||||
|
||||
const rowY = startY + rowIndex * (gridHeight + rowSpacing);
|
||||
|
||||
// 绘制左侧第一个网格
|
||||
const grid1X = margin;
|
||||
this.drawGrid(
|
||||
ctx,
|
||||
grid1X,
|
||||
rowY,
|
||||
cellSize,
|
||||
gridSize,
|
||||
rowData.grid1,
|
||||
gridData.color,
|
||||
);
|
||||
|
||||
// 绘制运算符
|
||||
const operatorStartX = grid1X + gridWidth + gridSpacing;
|
||||
const operatorX = operatorStartX + operatorWidth / 2;
|
||||
const operatorY = rowY + gridHeight / 2;
|
||||
const operatorSize = 30; // 运算符大小
|
||||
this.drawSymbol(
|
||||
operatorX,
|
||||
operatorY,
|
||||
rowData.operator,
|
||||
operatorSize,
|
||||
);
|
||||
|
||||
// 绘制左侧第二个网格
|
||||
const grid2X = operatorStartX + operatorWidth + gridSpacing;
|
||||
this.drawGrid(
|
||||
ctx,
|
||||
grid2X,
|
||||
rowY,
|
||||
cellSize,
|
||||
gridSize,
|
||||
rowData.grid2,
|
||||
gridData.color,
|
||||
);
|
||||
|
||||
// 绘制左侧圆点(垂直居中)
|
||||
const leftDotX = grid2X + gridWidth + dotSpacing;
|
||||
const leftDotY = rowY + gridHeight / 2;
|
||||
this.drawDot(ctx, leftDotX, leftDotY, dotRadius);
|
||||
|
||||
// 绘制右侧答案选项(随机乱序)
|
||||
const answerData = shuffledAnswers[rowIndex];
|
||||
if (answerData) {
|
||||
// 绘制右侧圆点(垂直居中)
|
||||
const rightDotX = rightAreaStartX - dotSpacing - dotRadius * 2;
|
||||
const rightDotY = rowY + gridHeight / 2;
|
||||
this.drawDot(ctx, rightDotX, rightDotY, dotRadius);
|
||||
|
||||
// 绘制答案网格
|
||||
this.drawGrid(
|
||||
ctx,
|
||||
rightAreaStartX,
|
||||
rowY,
|
||||
cellSize,
|
||||
gridSize,
|
||||
answerData.positions,
|
||||
gridData.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制3x3网格
|
||||
*/
|
||||
private drawGrid(
|
||||
ctx: RenderingContext,
|
||||
x: number,
|
||||
y: number,
|
||||
cellSize: number,
|
||||
gridSize: number,
|
||||
filledPositions: GridPosition[],
|
||||
fillColor: string,
|
||||
) {
|
||||
// 绘制网格线
|
||||
this.drawGridLines(x, y, cellSize, cellSize, gridSize, gridSize);
|
||||
|
||||
// 绘制填充的格子
|
||||
ctx.fillStyle = fillColor;
|
||||
for (const pos of filledPositions) {
|
||||
const cellX = x + pos.x * cellSize;
|
||||
const cellY = y + pos.y * cellSize;
|
||||
ctx.fillRect(cellX, cellY, cellSize, cellSize);
|
||||
}
|
||||
|
||||
// 重新绘制网格线(确保在填充色之上)
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.setLineDash([]);
|
||||
this.drawGridLines(x, y, cellSize, cellSize, gridSize, gridSize);
|
||||
}
|
||||
}
|
||||
|
||||
export default GridReasoningDraw;
|
||||
Reference in New Issue
Block a user