feat:V2.5.2 颜色找规律
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* 颜色找规律绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import {
|
||||
drawShapeSymbolShape,
|
||||
DrawShapeOptions,
|
||||
} from '../shapes/shapeSymbolShapes';
|
||||
|
||||
/**
|
||||
* 颜色找规律数据
|
||||
*/
|
||||
export interface ColorPatternData {
|
||||
/** 8组数据,每组包含图形ID和10个格子的颜色(前4个有颜色,后6个为null) */
|
||||
groups: Array<{
|
||||
shapeId: string;
|
||||
colors: Array<string | null>; // 长度为10,前4个有颜色,后6个为null
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色找规律绘制服务
|
||||
*/
|
||||
class ColorPatternDraw extends BaseDrawService {
|
||||
gridData: ColorPatternData | null = null;
|
||||
|
||||
constructor(
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) {
|
||||
super(canvas, ctx, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制颜色找规律内容
|
||||
*/
|
||||
async draw(gridData: ColorPatternData) {
|
||||
if (!gridData || !gridData.groups) {
|
||||
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: ColorPatternData;
|
||||
canvasWidth: number;
|
||||
startY: number;
|
||||
}): void {
|
||||
const { ctx, gridData, canvasWidth } = params;
|
||||
let { startY } = params;
|
||||
|
||||
startY = startY + 25;
|
||||
|
||||
const margin = 40; // 左右边距
|
||||
const cols = 10; // 每行10列
|
||||
const cellSize = 50; // 每个格子的宽度
|
||||
const gridWidth = cellSize * cols;
|
||||
const groupSpacing = 40; // 每组之间的间距
|
||||
|
||||
// 计算起始X位置(居中)
|
||||
const startX = margin + (canvasWidth - margin * 2 - gridWidth) / 2;
|
||||
|
||||
// 绘制8组
|
||||
for (
|
||||
let groupIndex = 0;
|
||||
groupIndex < gridData.groups.length;
|
||||
groupIndex++
|
||||
) {
|
||||
const group = gridData.groups[groupIndex];
|
||||
if (!group) continue;
|
||||
|
||||
const groupY = startY + groupIndex * (cellSize + groupSpacing);
|
||||
|
||||
// 绘制网格线(只绘制这一行的网格)
|
||||
this.drawGridLines(
|
||||
startX,
|
||||
groupY,
|
||||
cellSize,
|
||||
cellSize,
|
||||
cols,
|
||||
1, // 只有1行
|
||||
);
|
||||
|
||||
// 绘制图形和颜色
|
||||
for (let col = 0; col < cols; col++) {
|
||||
const color = group.colors[col];
|
||||
const colX = startX + col * cellSize;
|
||||
const cellCenterX = colX + cellSize / 2;
|
||||
const cellCenterY = groupY + cellSize / 2;
|
||||
const shapeSize = 35;
|
||||
|
||||
if (color) {
|
||||
// 前4个图形:绘制带颜色的图形
|
||||
|
||||
drawShapeSymbolShape(
|
||||
ctx,
|
||||
group.shapeId,
|
||||
cellCenterX,
|
||||
cellCenterY,
|
||||
shapeSize,
|
||||
color,
|
||||
);
|
||||
} else {
|
||||
// 根据图形ID绘制轮廓
|
||||
this.drawShapeOutline(
|
||||
ctx,
|
||||
group.shapeId,
|
||||
cellCenterX,
|
||||
cellCenterY,
|
||||
shapeSize,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制图形轮廓(用于空白图形)
|
||||
* 复用 shapeSymbolShapes.ts 中的绘制逻辑
|
||||
*/
|
||||
private drawShapeOutline(
|
||||
ctx: RenderingContext,
|
||||
shapeId: string,
|
||||
x: number,
|
||||
y: number,
|
||||
size: number,
|
||||
): void {
|
||||
const options: DrawShapeOptions = {
|
||||
fillColor: null,
|
||||
strokeColor: '#000',
|
||||
strokeWidth: 1.5,
|
||||
};
|
||||
// 使用新的调用方式,直接传入 options
|
||||
drawShapeSymbolShape(ctx, shapeId, x, y, size, options);
|
||||
}
|
||||
}
|
||||
|
||||
export default ColorPatternDraw;
|
||||
@@ -80,7 +80,7 @@ class PositionColoringDraw extends BaseDrawService {
|
||||
|
||||
const margin = 40; // 左右边距
|
||||
const gridSize = 3; // 3x3网格
|
||||
const cellSize = 65; // 每个格子的大小
|
||||
const cellSize = 65; // 每个格子的宽度
|
||||
const gridWidth = cellSize * gridSize;
|
||||
const gridHeight = cellSize * gridSize;
|
||||
|
||||
@@ -91,12 +91,11 @@ class PositionColoringDraw extends BaseDrawService {
|
||||
|
||||
// 绘制参考网格的网格线
|
||||
this.drawGridLines(
|
||||
ctx,
|
||||
referenceGridX,
|
||||
referenceGridY,
|
||||
gridWidth,
|
||||
gridHeight,
|
||||
cellSize,
|
||||
cellSize,
|
||||
gridSize,
|
||||
gridSize,
|
||||
);
|
||||
|
||||
@@ -105,7 +104,7 @@ class PositionColoringDraw extends BaseDrawService {
|
||||
const row = Math.floor(i / 3);
|
||||
const col = i % 3;
|
||||
const imageIndex = gridData.referenceGrid[i];
|
||||
const imageX = referenceGridX + col * cellSize + cellSize / 2 - 25; // 图片居中,假设图片大小50x50
|
||||
const imageX = referenceGridX + col * cellSize + cellSize / 2 - 25;
|
||||
const imageY = referenceGridY + row * cellSize + cellSize / 2 - 25;
|
||||
|
||||
try {
|
||||
@@ -179,12 +178,11 @@ class PositionColoringDraw extends BaseDrawService {
|
||||
|
||||
// 绘制网格线
|
||||
this.drawGridLines(
|
||||
ctx,
|
||||
taskGridX,
|
||||
taskGridY,
|
||||
taskGridWidth,
|
||||
taskGridHeight,
|
||||
taskGridSize,
|
||||
taskGridSize,
|
||||
gridSize,
|
||||
gridSize,
|
||||
);
|
||||
|
||||
@@ -206,41 +204,6 @@ class PositionColoringDraw extends BaseDrawService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制网格线
|
||||
*/
|
||||
private drawGridLines(
|
||||
ctx: RenderingContext,
|
||||
gridStartX: number,
|
||||
gridStartY: number,
|
||||
gridWidth: number,
|
||||
gridHeight: number,
|
||||
cellSize: number,
|
||||
gridSize: number,
|
||||
): void {
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.setLineDash([]); // 实线
|
||||
|
||||
// 绘制垂直线
|
||||
for (let i = 0; i <= gridSize; i++) {
|
||||
const x = gridStartX + i * cellSize;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, gridStartY);
|
||||
ctx.lineTo(x, gridStartY + gridHeight);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// 绘制水平线
|
||||
for (let i = 0; i <= gridSize; i++) {
|
||||
const y = gridStartY + i * cellSize;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(gridStartX, y);
|
||||
ctx.lineTo(gridStartX + gridWidth, y);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PositionColoringDraw;
|
||||
|
||||
@@ -84,9 +84,8 @@ class ShapeSymbolDraw extends BaseDrawService {
|
||||
const margin = 40; // 左右边距
|
||||
const legendCols = 4; // 图例列数
|
||||
const legendRows = 2; // 图例行数
|
||||
const cellSize = 65; // 每个格子的大小
|
||||
const cellSize = 65; // 每个格子的宽度
|
||||
const gridWidth = cellSize * legendCols;
|
||||
const gridHeight = cellSize * legendRows;
|
||||
|
||||
// 绘制参考网格(上面的九宫格)
|
||||
const legendStartX =
|
||||
@@ -95,11 +94,9 @@ class ShapeSymbolDraw extends BaseDrawService {
|
||||
|
||||
// 绘制参考网格的网格线
|
||||
this.drawGridLines(
|
||||
ctx,
|
||||
legendStartX,
|
||||
legendStartY,
|
||||
gridWidth,
|
||||
gridHeight,
|
||||
cellSize,
|
||||
cellSize,
|
||||
legendCols,
|
||||
legendRows,
|
||||
@@ -113,7 +110,7 @@ class ShapeSymbolDraw extends BaseDrawService {
|
||||
gridData.legendMapping.forEach((mapping, index: number) => {
|
||||
if (!mapping) return;
|
||||
|
||||
const shapeX = legendStartX + index * cellSize + cellSize / 2; // 图片居中,假设图片大小50x50
|
||||
const shapeX = legendStartX + index * cellSize + cellSize / 2;
|
||||
const shapeY = legendStartY + cellSize / 2;
|
||||
|
||||
const shapeSize = 42;
|
||||
@@ -128,8 +125,8 @@ class ShapeSymbolDraw extends BaseDrawService {
|
||||
);
|
||||
|
||||
// 第二行:绘制符号(使用 emoji)
|
||||
const symbolX = legendStartX + index * cellSize + cellSize / 2; // 图片居中,假设图片大小50x50
|
||||
const symbolY = legendStartY + cellSize + cellSize / 2 + 5;
|
||||
const symbolX = legendStartX + index * cellSize + cellSize / 2;
|
||||
const symbolY = legendStartY + cellSize + cellSize / 2;
|
||||
this.drawSymbol(ctx, symbolX, symbolY, mapping.symbol, 30);
|
||||
});
|
||||
|
||||
@@ -140,9 +137,8 @@ class ShapeSymbolDraw extends BaseDrawService {
|
||||
const practiceStartY = dividerY + 30;
|
||||
const practiceCols = 8;
|
||||
const practiceRows = 8;
|
||||
const practiceCellSize = 60; // 练习格子大小(正方形)
|
||||
const practiceCellSize = 60; // 练习格子宽度
|
||||
const practiceGridWidth = practiceCellSize * practiceCols;
|
||||
const practiceGridHeight = practiceCellSize * practiceRows;
|
||||
|
||||
// 计算练习区域起始X位置(居中)
|
||||
const practiceStartX =
|
||||
@@ -150,11 +146,9 @@ class ShapeSymbolDraw extends BaseDrawService {
|
||||
|
||||
// 绘制练习区域网格线(使用 drawGridLines)
|
||||
this.drawGridLines(
|
||||
ctx,
|
||||
practiceStartX,
|
||||
practiceStartY,
|
||||
practiceGridWidth,
|
||||
practiceGridHeight,
|
||||
practiceCellSize,
|
||||
practiceCellSize,
|
||||
practiceCols,
|
||||
practiceRows,
|
||||
@@ -195,71 +189,88 @@ class ShapeSymbolDraw extends BaseDrawService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制符号(使用 emoji 文字)
|
||||
* 绘制符号(使用路径绘制,类似 shapeSymbolShapes.ts)
|
||||
*/
|
||||
private drawSymbol(
|
||||
ctx: RenderingContext,
|
||||
x: number,
|
||||
y: number,
|
||||
symbol: string,
|
||||
fontSize: number,
|
||||
size: number,
|
||||
): void {
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
|
||||
// 符号映射:将符号转换为 emoji
|
||||
const symbolMap: Record<string, string> = {
|
||||
'+': '➕',
|
||||
'-': '➖',
|
||||
'×': '✖️',
|
||||
'✓': '✔️',
|
||||
};
|
||||
const lineWidth = size * 0.15; // 线条宽度
|
||||
const halfSize = size / 2;
|
||||
const strokeLength = halfSize * 0.7; // 线条长度
|
||||
|
||||
const emojiSymbol = symbolMap[symbol] || symbol;
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineJoin = 'round';
|
||||
|
||||
ctx.font = `bold ${fontSize}px Arial`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillText(emojiSymbol, x, y);
|
||||
switch (symbol) {
|
||||
case '+':
|
||||
// 加号:横线和竖线
|
||||
ctx.beginPath();
|
||||
// 横线
|
||||
ctx.moveTo(-strokeLength, 0);
|
||||
ctx.lineTo(strokeLength, 0);
|
||||
// 竖线
|
||||
ctx.moveTo(0, -strokeLength);
|
||||
ctx.lineTo(0, strokeLength);
|
||||
ctx.stroke();
|
||||
break;
|
||||
|
||||
case '-':
|
||||
// 减号:横线
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-strokeLength, 0);
|
||||
ctx.lineTo(strokeLength, 0);
|
||||
ctx.stroke();
|
||||
break;
|
||||
|
||||
case '×':
|
||||
// 乘号:两条斜线
|
||||
ctx.beginPath();
|
||||
// 左上到右下
|
||||
ctx.moveTo(-strokeLength * 0.7, -strokeLength * 0.7);
|
||||
ctx.lineTo(strokeLength * 0.7, strokeLength * 0.7);
|
||||
// 右上到左下
|
||||
ctx.moveTo(strokeLength * 0.7, -strokeLength * 0.7);
|
||||
ctx.lineTo(-strokeLength * 0.7, strokeLength * 0.7);
|
||||
ctx.stroke();
|
||||
break;
|
||||
|
||||
case '✓':
|
||||
// 对号:勾,整体更大,右侧的线更长
|
||||
const checkScale = 1.2; // 对号整体放大1.2倍
|
||||
ctx.beginPath();
|
||||
const checkStartX = -strokeLength * 0.5 * checkScale;
|
||||
const checkStartY = -strokeLength * 0.2 * checkScale;
|
||||
const checkMidX = -strokeLength * 0.1 * checkScale;
|
||||
const checkMidY = strokeLength * 0.3 * checkScale;
|
||||
const checkEndX = strokeLength * 1.0 * checkScale; // 增加右侧长度
|
||||
const checkEndY = -strokeLength * 0.4 * checkScale; // 稍微向上调整
|
||||
ctx.moveTo(checkStartX, checkStartY);
|
||||
ctx.lineTo(checkMidX, checkMidY);
|
||||
ctx.lineTo(checkEndX, checkEndY);
|
||||
ctx.stroke();
|
||||
break;
|
||||
|
||||
default:
|
||||
// 默认绘制加号
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-strokeLength, 0);
|
||||
ctx.lineTo(strokeLength, 0);
|
||||
ctx.moveTo(0, -strokeLength);
|
||||
ctx.lineTo(0, strokeLength);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user