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

213 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 数字点连线绘制服务
*/
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;