/** * 格子仿画绘制服务 * 合并了基础绘制和内容绘制逻辑 */ 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, ) { super(canvas, ctx, options); } /** * 绘制格子仿画内容 */ async draw(gridData: GridDrawingData) { if (!gridData || !gridData.groups || gridData.groups.length === 0) { return; } this.gridData = gridData; this.prepareDraw(); await this.drawHeaderAndDivider(); 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 { const { ctx, gridData, canvasWidth } = params; let { startY } = params; const { config, groups, groupsPerRow, totalRows } = gridData; const { rows, cols } = config; startY = startY + 30; // 计算网格尺寸 const margin = 40; // 左右边距 const gridSpacing = 20; // 填充网格和空网格之间的间距 const groupSpacing = 40; // 两组之间的间距(一行中两组格子仿画中间) const rowSpacing = 50; // 行之间的间距 const dividerWidth = 1; // 分割线宽度 // 计算每组网格的尺寸 const availableWidth = canvasWidth - margin * 2; const totalGridsPerRow = groupsPerRow * 2; // 每组包含填充和空两个网格 const gridWidth = (availableWidth - (groupsPerRow - 1) * groupSpacing - (totalGridsPerRow - 1) * gridSpacing) / totalGridsPerRow; const cellSize = Math.floor(gridWidth / cols); const actualGridWidth = cellSize * cols; const gridHeight = cellSize * rows; const firstRowStartY = startY + 30; // 顶部间距 const lastRowEndY = firstRowStartY + totalRows * gridHeight + (totalRows - 1) * rowSpacing; // 计算第一行的起始X位置(居中) const rowTotalWidth = totalGridsPerRow * actualGridWidth + (totalGridsPerRow - 1) * gridSpacing + (groupsPerRow - 1) * groupSpacing; const rowStartX = margin + (availableWidth - rowTotalWidth) / 2; // 计算中间垂直分割线的X位置(两组之间的位置) const middleDividerX = groupsPerRow > 1 ? rowStartX + actualGridWidth + gridSpacing + actualGridWidth + groupSpacing / 2 : null; // 遍历每一行 for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) { const rowStartIndex = rowIndex * groupsPerRow; const rowGroups = groups.slice( rowStartIndex, rowStartIndex + groupsPerRow, ); const currentY = firstRowStartY + rowIndex * (gridHeight + rowSpacing); let currentX = rowStartX; // 遍历当前行的每一组 for ( let groupIndex = 0; groupIndex < rowGroups.length; groupIndex++ ) { const group = rowGroups[groupIndex]; // 绘制填充的网格(左侧) this.drawSingleGrid({ ctx, gridStartX: currentX, gridStartY: currentY, gridWidth: actualGridWidth, gridHeight, cellSize, rows, cols, cells: group.filledCells, config, }); currentX += actualGridWidth + gridSpacing; // 绘制空的网格(右侧) this.drawSingleGrid({ ctx, gridStartX: currentX, gridStartY: currentY, gridWidth: actualGridWidth, gridHeight, cellSize, rows, cols, cells: [], // 空网格 config, }); currentX += actualGridWidth; // 如果不是最后一组,添加组间距 if (groupIndex < rowGroups.length - 1) { currentX += groupSpacing; } } // 如果不是最后一行,绘制水平分割线 if (rowIndex < totalRows - 1) { const lineY = currentY + gridHeight + rowSpacing / 2; ctx.strokeStyle = '#999'; ctx.lineWidth = dividerWidth; ctx.setLineDash([4, 4]); // 虚线 ctx.beginPath(); ctx.moveTo(margin, lineY); ctx.lineTo(canvasWidth - margin, lineY); ctx.stroke(); ctx.setLineDash([]); // 重置为实线 } } // 绘制中间垂直分割线(贯穿所有行) if (middleDividerX !== null && groupsPerRow > 1) { ctx.strokeStyle = '#999'; ctx.lineWidth = dividerWidth; ctx.setLineDash([4, 4]); // 虚线 ctx.beginPath(); ctx.moveTo(middleDividerX, firstRowStartY); ctx.lineTo(middleDividerX, lastRowEndY); ctx.stroke(); ctx.setLineDash([]); // 重置为实线 } } /** * 绘制单个网格 */ 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 || '#999'; 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;