213 lines
7.2 KiB
TypeScript
213 lines
7.2 KiB
TypeScript
/**
|
||
* 数字点连线绘制服务
|
||
*/
|
||
|
||
import { BaseDrawService } from '../../../core/draw/baseDraw';
|
||
|
||
/**
|
||
* 数字点连线数据
|
||
*/
|
||
export interface DotConnectData {
|
||
/** 参考区域:数字到颜色的映射(1-9) */
|
||
colorMap: Array<{
|
||
number: number; // 数字(1-9)
|
||
color: string;
|
||
}>;
|
||
/** 内容区域:9个组的题目 */
|
||
groups: Array<{
|
||
/** 数字序列 */
|
||
sequence: number[];
|
||
}>;
|
||
}
|
||
|
||
/**
|
||
* 数字点连线绘制服务
|
||
*/
|
||
class DotConnectDraw extends BaseDrawService {
|
||
dotConnectData: DotConnectData | null = null;
|
||
|
||
constructor(
|
||
canvas: Canvas,
|
||
ctx: RenderingContext,
|
||
options?: Record<string, any>,
|
||
) {
|
||
super(canvas, ctx, options);
|
||
}
|
||
|
||
/**
|
||
* 绘制数字点连线内容
|
||
*/
|
||
async draw(dotConnectData: DotConnectData) {
|
||
if (
|
||
!dotConnectData ||
|
||
!dotConnectData.colorMap ||
|
||
!dotConnectData.groups
|
||
) {
|
||
return;
|
||
}
|
||
|
||
this.dotConnectData = dotConnectData;
|
||
this.prepareDraw();
|
||
await this.drawHeaderAndDivider();
|
||
await this.drawContent({
|
||
canvas: this.canvas,
|
||
ctx: this.ctx,
|
||
dotConnectData: this.dotConnectData,
|
||
canvasWidth: this.canvasWidth,
|
||
startY: this.currentY,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 绘制内容区域
|
||
*/
|
||
private async drawContent(params: {
|
||
canvas: WechatMiniprogram.Canvas;
|
||
ctx: RenderingContext;
|
||
dotConnectData: DotConnectData;
|
||
canvasWidth: number;
|
||
startY: number;
|
||
}): Promise<void> {
|
||
const { ctx, dotConnectData, canvasWidth } = params;
|
||
let { startY } = params;
|
||
|
||
startY = startY + 25;
|
||
|
||
const margin = 40; // 左右边距
|
||
|
||
// ========== 上部参考区域:3x3 九宫格 ==========
|
||
const referenceStartY = startY;
|
||
const referenceCellSize = 38; // 每个方格的大小
|
||
const referenceGridWidth = referenceCellSize * 3; // 网格总宽度(3列)
|
||
const referenceGridHeight = referenceCellSize * 3; // 网格总高度(3行)
|
||
|
||
// 计算参考区域的起始X坐标(居中)
|
||
const referenceStartX =
|
||
margin + (canvasWidth - margin * 2 - referenceGridWidth) / 2;
|
||
|
||
// 使用drawGridLines绘制网格(3行3列)
|
||
this.drawGridLines(
|
||
referenceStartX,
|
||
referenceStartY,
|
||
referenceCellSize,
|
||
referenceCellSize,
|
||
3,
|
||
3,
|
||
);
|
||
|
||
// 绘制数字(1-9),每个数字使用对应的颜色
|
||
for (let row = 0; row < 3; row++) {
|
||
for (let col = 0; col < 3; col++) {
|
||
const number = row * 3 + col + 1; // 1-9
|
||
const cellX = referenceStartX + col * referenceCellSize;
|
||
const cellY = referenceStartY + row * referenceCellSize;
|
||
|
||
// 查找数字对应的颜色
|
||
const colorInfo = dotConnectData.colorMap.find(
|
||
(m) => m.number === number,
|
||
);
|
||
const color = colorInfo?.color || '#000';
|
||
|
||
// 绘制数字(使用对应颜色)
|
||
ctx.fillStyle = color;
|
||
ctx.font = 'bold 24px Arial';
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
ctx.fillText(
|
||
number.toString(),
|
||
cellX + referenceCellSize / 2,
|
||
cellY + referenceCellSize / 2,
|
||
);
|
||
}
|
||
}
|
||
|
||
// ========== 虚线分割 ==========
|
||
const dividerY = referenceStartY + referenceGridHeight + 20;
|
||
this.drawDashedDivider(dividerY, margin);
|
||
|
||
// ========== 下部内容区域:9个组 ==========
|
||
const contentStartY = dividerY + 20;
|
||
const dotSpacing = 35; // 圆点间距
|
||
const dotRadius = 6; // 圆点半径
|
||
const boxPadding = 20; // 框内边距
|
||
const boxWidth = dotSpacing * 2 + boxPadding * 2; // 框的宽度(3个圆点,2个间距)
|
||
const boxHeight = boxWidth; // 框的高度(正方形)
|
||
const textHeight = 25; // 数字文本的高度
|
||
const textBoxSpacing = 12; // 数字文本与框的间距
|
||
const groupSpacingX = 60; // 一行中两组之间的间距
|
||
const groupSpacingY = 30; // 行之间的间距
|
||
|
||
// 计算每组的总宽度和高度
|
||
const groupWidth = boxWidth;
|
||
const groupHeight = textHeight + textBoxSpacing + boxHeight;
|
||
const totalGroupsWidth = 3 * groupWidth + 2 * groupSpacingX;
|
||
const groupsStartX =
|
||
margin + (canvasWidth - margin * 2 - totalGroupsWidth) / 2;
|
||
|
||
// 绘制9个组(三行三列)
|
||
for (let row = 0; row < 3; row++) {
|
||
for (let col = 0; col < 3; col++) {
|
||
const groupIndex = row * 3 + col;
|
||
const group = dotConnectData.groups[groupIndex];
|
||
if (!group) continue;
|
||
|
||
const groupX =
|
||
groupsStartX + col * (groupWidth + groupSpacingX);
|
||
const groupY =
|
||
contentStartY + row * (groupHeight + groupSpacingY);
|
||
|
||
// 绘制数字序列(在框正上方,居中)
|
||
const sequenceText = group.sequence.join('-');
|
||
ctx.fillStyle = '#000';
|
||
ctx.font = '18px Arial';
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'bottom';
|
||
const textX = groupX + groupWidth / 2;
|
||
const textY = groupY + textHeight;
|
||
ctx.fillText(sequenceText, textX, textY);
|
||
|
||
// 绘制虚线圆角框
|
||
const boxX = groupX;
|
||
const boxY = groupY + textHeight + textBoxSpacing;
|
||
this.drawRoundedRect(boxX, boxY, boxWidth, boxHeight, {
|
||
isDashed: true,
|
||
color: '#999',
|
||
lineWidth: 1,
|
||
radius: 10,
|
||
});
|
||
|
||
// 计算框内圆点的起始位置(居中)
|
||
const dotsStartX = boxX + boxPadding;
|
||
const dotsStartY = boxY + boxPadding;
|
||
|
||
// 绘制3x3圆点网格
|
||
for (let dotRow = 0; dotRow < 3; dotRow++) {
|
||
for (let dotCol = 0; dotCol < 3; dotCol++) {
|
||
const dotNumber = dotRow * 3 + dotCol + 1; // 1-9
|
||
const dotX = dotsStartX + dotCol * dotSpacing;
|
||
const dotY = dotsStartY + dotRow * dotSpacing;
|
||
|
||
// 查找数字对应的颜色
|
||
const colorInfo = dotConnectData.colorMap.find(
|
||
(m) => m.number === dotNumber,
|
||
);
|
||
const dotColor = colorInfo?.color || '#000';
|
||
|
||
// 绘制圆点(黑色边框)
|
||
this.drawDot(
|
||
ctx,
|
||
dotX,
|
||
dotY,
|
||
dotRadius,
|
||
dotColor,
|
||
'#000',
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
export default DotConnectDraw;
|