Files
doodle-mini/miniprogram/focusPages/shared/service/gridDraw.ts
T
2025-12-12 17:19:36 +08:00

260 lines
7.6 KiB
TypeScript

/**
* 格子仿画绘制服务
* 合并了基础绘制和内容绘制逻辑
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { GridCell, GridConfig } from '../types/gridTypes';
/**
* 格子组数据(一组包含一个填充的网格和一个空的网格)
*/
export interface GridGroup {
/** 填充的格子数据 */
filledCells: GridCell[];
/** 空的格子(用于仿画) */
emptyCells: GridCell[]; // 始终为空数组,仅用于标识
}
/**
* 格子仿画数据
*/
export interface GridDrawingData {
config: GridConfig;
/** 格子组数组,每组包含一个填充的网格和一个空的网格 */
groups: GridGroup[];
/** 每行展示的组数 */
groupsPerRow: number;
/** 总行数 */
totalRows: number;
}
/**
* 格子仿画绘制服务
*/
class GridDraw extends BaseDrawService {
gridData: GridDrawingData | null = null;
constructor(
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) {
super(canvas, ctx, options);
}
/**
* 绘制格子仿画内容
*/
async draw(gridData: GridDrawingData) {
if (!gridData || !gridData.groups || gridData.groups.length === 0) {
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.drawGridContent({
canvas: this.canvas,
ctx: this.ctx,
gridData: this.gridData,
canvasWidth: this.canvasWidth,
startY: this.currentY,
});
}
/**
* 绘制网格内容
*/
private async drawGridContent(params: {
canvas: WechatMiniprogram.Canvas;
ctx: RenderingContext;
gridData: GridDrawingData;
canvasWidth: number;
startY: number;
}): Promise<void> {
const { ctx, gridData, canvasWidth, startY } = params;
const { config, groups, groupsPerRow, totalRows } = gridData;
const { rows, cols } = config;
// 计算网格尺寸
const margin = 40; // 左右边距
const groupSpacing = 20; // 组之间的间距
const rowSpacing = 30; // 行之间的间距
const dividerWidth = 2; // 分割线宽度
// 计算每组网格的尺寸
const availableWidth = canvasWidth - margin * 2;
const totalGroupsPerRow = groupsPerRow * 2; // 每组包含填充和空两个网格
const groupWidth =
(availableWidth -
(groupsPerRow - 1) * dividerWidth -
(totalGroupsPerRow - 1) * groupSpacing) /
totalGroupsPerRow;
const cellSize = Math.floor(groupWidth / cols);
const gridWidth = cellSize * cols;
const gridHeight = cellSize * rows;
let currentY = startY + 30; // 顶部间距
// 遍历每一行
for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) {
const rowStartIndex = rowIndex * groupsPerRow;
const rowGroups = groups.slice(
rowStartIndex,
rowStartIndex + groupsPerRow,
);
// 计算当前行的起始X位置(居中)
const rowTotalWidth =
totalGroupsPerRow * gridWidth +
(totalGroupsPerRow - 1) * groupSpacing +
(groupsPerRow - 1) * dividerWidth;
const rowStartX = margin + (availableWidth - rowTotalWidth) / 2;
let currentX = rowStartX;
// 遍历当前行的每一组
for (
let groupIndex = 0;
groupIndex < rowGroups.length;
groupIndex++
) {
const group = rowGroups[groupIndex];
// 绘制填充的网格(左侧)
this.drawSingleGrid({
ctx,
gridStartX: currentX,
gridStartY: currentY,
gridWidth,
gridHeight,
cellSize,
rows,
cols,
cells: group.filledCells,
config,
});
currentX += gridWidth + groupSpacing;
// 绘制空的网格(右侧)
this.drawSingleGrid({
ctx,
gridStartX: currentX,
gridStartY: currentY,
gridWidth,
gridHeight,
cellSize,
rows,
cols,
cells: [], // 空网格
config,
});
currentX += gridWidth;
// 如果不是最后一组,绘制垂直分割线
if (groupIndex < rowGroups.length - 1) {
ctx.strokeStyle = '#000000';
ctx.lineWidth = dividerWidth;
ctx.beginPath();
ctx.moveTo(currentX, currentY);
ctx.lineTo(currentX, currentY + gridHeight);
ctx.stroke();
currentX += dividerWidth;
}
}
currentY += gridHeight + rowSpacing;
// 如果不是最后一行,绘制水平分割线
if (rowIndex < totalRows - 1) {
ctx.strokeStyle = '#000000';
ctx.lineWidth = dividerWidth;
ctx.beginPath();
ctx.moveTo(margin, currentY - rowSpacing / 2);
ctx.lineTo(canvasWidth - margin, currentY - rowSpacing / 2);
ctx.stroke();
}
}
}
/**
* 绘制单个网格
*/
private drawSingleGrid(params: {
ctx: RenderingContext;
gridStartX: number;
gridStartY: number;
gridWidth: number;
gridHeight: number;
cellSize: number;
rows: number;
cols: number;
cells: GridCell[];
config: GridConfig;
}): void {
const {
ctx,
gridStartX,
gridStartY,
gridWidth,
gridHeight,
cellSize,
rows,
cols,
cells,
config,
} = params;
// 绘制网格线
ctx.strokeStyle = config.gridLineColor || '#000000';
ctx.lineWidth = config.gridLineWidth || 1;
ctx.setLineDash([]); // 实线
// 绘制垂直线
for (let i = 0; i <= cols; i++) {
const x = gridStartX + i * cellSize;
ctx.beginPath();
ctx.moveTo(x, gridStartY);
ctx.lineTo(x, gridStartY + gridHeight);
ctx.stroke();
}
// 绘制水平线
for (let i = 0; i <= rows; i++) {
const y = gridStartY + i * cellSize;
ctx.beginPath();
ctx.moveTo(gridStartX, y);
ctx.lineTo(gridStartX + gridWidth, y);
ctx.stroke();
}
// 绘制填充的格子
for (const cell of cells) {
if (cell.x >= 0 && cell.x < cols && cell.y >= 0 && cell.y < rows) {
const x = gridStartX + cell.x * cellSize;
const y = gridStartY + cell.y * cellSize;
// 填充颜色
ctx.fillStyle = cell.color;
ctx.fillRect(x + 1, y + 1, cellSize - 2, cellSize - 2);
}
}
}
}
export default GridDraw;