/** * 找一找涂色,文字涂色服务 * 根据文字列表绘制到Canvas */ import { BaseDrawService } from '../core/draw/baseDraw'; /** * 计算示例区域圆的中心点 * @param canvasWidth 画布宽度(逻辑像素) * @param circleCount 圆的数量 * @param padding 圆的间距(逻辑像素) * @param radius 圆的半径(逻辑像素) * @returns 圆的中心点 */ function calculateCircleCenters( canvasWidth: number, circleCount: number, padding: number, radius: number, ) { const availableWidth = canvasWidth - 2 * padding; const maxSpacing = 5.5 * radius; const centers = []; if (circleCount === 1) { centers.push(canvasWidth / 2); } else { const requiredSpace = 2 * radius * circleCount; const spaceBetween = (availableWidth - requiredSpace) / (circleCount - 1); if (spaceBetween > maxSpacing) { const totalWidth = 2 * radius + (circleCount - 1) * maxSpacing; const startX = (canvasWidth - totalWidth) / 2 + radius; for (let i = 0; i < circleCount; i++) { centers.push(startX + i * maxSpacing); } } else { const startX = padding + radius; for (let i = 0; i < circleCount; i++) { centers.push(startX + i * (spaceBetween + 2 * radius)); } } } return centers; } class TextDrawService extends BaseDrawService { colors: string[]; characters: string[]; constructor( canvas: Canvas, ctx: RenderingContext, options?: Record, ) { options = options || {}; super(canvas, ctx, { title: '找一找 涂 色', subTitle: '给文字涂上相同的颜色', ...options, }); this.colors = ['#000']; this.characters = ['王']; } async draw(list: Array<{ color: string; word: string }>) { this.setPrintConfig(); this.colors = list.map((item) => item.color || '#000'); this.characters = list.map((item) => item.word || '日'); this.clear(); this.setPaper(); await this.drawHeader(); this.drawDivider(); this.drawLegend(); this.drawContent(); // await this.drawPrintFooter(); } /** 绘制示例 * 矩形: x、y为左上角 * 圆形:x、y为圆心 * 文字:x textAlign 为 start,文本左边缘对齐 * y textBaseline 为 alphabetic,y 对应字母基线(类似左下角) * * 注意:所有尺寸已转换为逻辑像素(除以3) * */ drawLegend() { const { ctx, colors, characters } = this; if (characters.length <= 0) return; const legendTopY = this.currentY; const radius = 22; // 65/3≈22 const rectWidth = 60; // 180/3=60 const rectHeight = 27; // 80/3≈27 const startX = 87 + radius; // (260+65)/3≈108 const startY = legendTopY + 10 + radius; // (30+65)/3≈32 const len = characters.length || 4; const centers = calculateCircleCenters( this.canvasWidth, len, 73, radius, ); // 220/3≈73 /** 计算每个圆之间的间距 */ const spaceWidth = len > 3 ? (this.canvasWidth - startX * 2) / (len - 1) : 0; ctx.moveTo(startX, startY); colors.forEach((color: string, index: number) => { const x = centers[index] || startX + index * spaceWidth; const y = startY; // 绘制圆 ctx.strokeStyle = '#000'; ctx.fillStyle = color; ctx.lineWidth = 1; // 4/3≈1 ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); ctx.closePath(); // 绘制长方形 ctx.fillStyle = '#fff'; ctx.strokeStyle = '#000'; ctx.lineWidth = 1; // 4/3≈1 const rectangleX = x - rectWidth / 2; const rectangleY = y + radius + 14; // 43/3≈14 ctx.strokeRect(rectangleX, rectangleY, rectWidth, rectHeight); // 绘制字符 ctx.fillStyle = '#333'; ctx.font = 'bold 16px "Microsoft Yahei"'; // 48/3=16 ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; ctx.fillText( characters[index], rectangleX + rectWidth / 2, rectangleY + rectHeight / 2, rectWidth, ); }); this.currentY = startY + radius + 14 + rectHeight + 24; this.drawDivider(); } drawContent() { const { ctx, characters } = this; if (characters.length <= 0) return; const len = characters.length; const rows = 8; const radius = 27; // 80/3≈27 const fontSize = 24; // 72/3=24 const startY = this.currentY + 10 + radius; // 62/3≈21 const startX1 = 103 + radius; // 310/3≈103 const startX2 = 67 + radius; // 200/3≈67 const spaceWidthFrist = (this.canvasWidth - startX1 * 2) / (5 - 1); const spaceWidthSecond = (this.canvasWidth - startX2 * 2) / (6 - 1); const spaceHeight = 18; // 54/3=18 ctx.moveTo(startX1, startY); for (let i = 0; i < rows; i++) { const cols = i % 2 === 0 ? 5 : 6; for (let j = 0; j < cols; j++) { const y = startY + i * (spaceHeight + radius * 2); const x = i % 2 === 0 ? startX1 + j * spaceWidthFrist : startX2 + j * spaceWidthSecond; const character = characters[Math.floor(Math.random() * len)]; ctx.fillStyle = '#fff'; ctx.strokeStyle = '#000'; ctx.lineWidth = 1; // 4/3≈1 ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); ctx.closePath(); ctx.fillStyle = '#333'; ctx.font = `${fontSize}px "Microsoft Yahei"`; ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; ctx.fillText(character, x, y, radius * 2); } } } // drawLine 已由基类的 drawDivider 替代,保留此方法以保持兼容 drawLine(linY: number) { this.drawDivider(); } } export default TextDrawService;