// import { PAPER_SIZE } from './constant'; import { getMiniCodeImage } from './utils'; class TextDrawService { canvas: WechatMiniprogram.Canvas; ctx: RenderingContext; options: Record; paperSize: PaperSize; currentX: number; currentY: number; constructor(canvas: Canvas, ctx: RenderingContext, options?: Record) { this.canvas = canvas; this.ctx = ctx; this.paperSize = 'A4'; this.options = { appName: '涂鸦呀', appHint: '涂色|识字|画画|认知', title: '找一找 涂 色', subTitle: '把相同的文字涂上相同的颜色', radius: 36, colors: ['#2196F3', '#FFEB3B', '#FF9800', '#F44336', '#F44336'], characters: ['鱼', '鸟', '猫', '狗', '牛'], ...options, }; this.currentX = 0; this.currentY = 0; } draw() { this.setPaper(); // this.setPaper3(); this.drawHeader(); // this.drawLegend(); } async drawHeader() { const { canvas, ctx } = this; const { appName, appHint, title, subTitle } = this.options; this.currentX = 60; this.currentY = 60; const titleX = this.currentX + 140 + 14; const titleY = 87; const image = await getMiniCodeImage(canvas); ctx.drawImage(image, 60, 60, 140, 140); ctx.font = 'bold 36px "Microsoft Yahei"'; ctx.fillStyle = '#000'; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; ctx.fillText(appName, titleX, titleY); ctx.font = '24px "Microsoft Yahei"'; ctx.fillStyle = '#666'; ctx.fillText(appHint, titleX, 163); // 绘制分割线 ctx.strokeStyle = '#888'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(483, 70); ctx.lineTo(483, 70 + 140); ctx.stroke(); ctx.font = 'bold 36px "Microsoft Yahei"'; ctx.fillStyle = '#000'; ctx.fillText(title, 538, titleY); ctx.font = '24px "Microsoft Yahei"'; ctx.fillStyle = '#666'; ctx.fillText(subTitle, 538, 163); this.drawLegend(); } /** 绘制示例 * 矩形: x、y为左上角 * 圆形:x、y为圆心 * 文字:x textAlign 为 start,文本左边缘对齐 * y textBaseline 为 alphabetic,y 对应字母基线(类似左下角) * * */ drawLegend() { const { canvas, ctx, options } = this; const { colors, characters, radius } = options; const rectWidth = 100; const rectHeight = 48; const startX = 978; const startY = 60 + radius; // 设置起始Y坐标 const len = characters.length || 4; const canvasWidth = canvas.width; /** 计算每个圆之间的间距 俩个60,一个是页面右侧边距,另一个是圆离右边距地边距*/ const spaceWidth = (canvasWidth - startX - 60 - 60 - radius * (len - 1)) / (len - 1); ctx.moveTo(startX, startY); colors.forEach((color: string, index: number) => { const x = startX + index * (spaceWidth + radius); const y = startY; // 计算圆的Y坐标 // 绘制圆 ctx.strokeStyle = '#333'; ctx.fillStyle = color; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); ctx.closePath(); // 绘制长方形 ctx.fillStyle = '#fff'; // 长方形背景色 ctx.strokeStyle = '#333'; ctx.lineWidth = 2; const rectangleX = x - rectWidth / 2; // 从圆形移动一半的长方形的长 const rectangleY = y + radius + 18; // 从圆形下移一个半径,再加上18的间距 ctx.strokeRect(rectangleX, rectangleY, rectWidth, rectHeight); // 绘制长方形 // 绘制字符 ctx.fillStyle = '#333'; ctx.font = 'bold 24px "Microsoft Yahei"'; ctx.textBaseline = 'top'; ctx.fillText(characters[index], rectangleX + 38, 163); }); this.currentY = 220; // 计算分割线的Y坐标,距离示例图20px this.drawLine(this.currentY); } drawLine(linY: number) { const { canvas, ctx } = this; ctx.strokeStyle = '#333'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(20, linY); ctx.lineTo(canvas.width - 20, linY); ctx.stroke(); } setPaper() { const { ctx, canvas } = this; const { pixelRatio: dpr } = wx.getWindowInfo(); const { width, height } = { width: 595 * dpr, height: 842 * dpr }; canvas.width = width; canvas.height = height; this.clear(); ctx.fillStyle = '#fff'; // 设置背景色为白色 ctx.fillRect(0, 0, width, height); } setPaper3() { const { ctx, canvas } = this; const { pixelRatio: dpr } = wx.getWindowInfo(); // @ts-ignore const winWidth = canvas._width; // @ts-ignore const winHeight = canvas._height; // 容器逻辑尺寸 // 计算适配后的物理尺寸 const targetWidth = winWidth * dpr; // 宽度100%适配容器 const aspectRatio = 842 / 595; // 原图宽高比(示例为A4纸比例) let targetHeight = targetWidth * aspectRatio; // 按比例计算高度 // 判断是否需要裁剪高度 if (targetHeight > winHeight * dpr) { targetHeight = winHeight * dpr; // 若超出容器高度则强制裁剪 } // 设置 Canvas 物理像素 canvas.width = targetWidth; canvas.height = targetHeight; console.log('targetWidth---:', targetWidth); console.log('targetHeight---:', targetHeight); this.clear(); ctx.fillStyle = '#fff'; // 设置背景色为白色 ctx.fillRect(0, 0, targetWidth, targetHeight); } setPaper2() { const { canvas, ctx } = this; const { pixelRatio: dpr } = wx.getWindowInfo(); const { width, height } = { width: 595 * dpr, height: 842 * dpr }; // @ts-ignore const winWidth = canvas._width; // @ts-ignore const winHeight = canvas._height; const canvasAspect = width / height; // Canvas 内容的宽高比 const containerAspect = winHeight / winWidth; // 容器的宽高比 // 选择缩放比例较小的方向(确保内容完全显示) let scaleRatio = containerAspect > canvasAspect ? winWidth / width // 以宽度为基准 : winHeight / height; // 以高度为基准 console.log('scaleRatio-----:', scaleRatio); const scaledWidth = width * scaleRatio; const scaledHeight = height * scaleRatio; // 避免超过小程序画布最大限制(4096) if (scaledWidth > 4096 || scaledHeight > 4096) { scaleRatio *= Math.min(4096 / scaledWidth, 4096 / scaledHeight); } console.log('scaledWidth-----:', scaledWidth); console.log('scaledHeight-----:', scaledHeight); canvas.width = scaledWidth; canvas.height = scaledHeight; ctx.translate((winWidth * dpr) / 2, (winHeight * dpr) / 2); ctx.scale(scaleRatio * dpr, scaleRatio * dpr); ctx.translate(-width / 2, -height / 2); // 将内容原点移至中心 // this.canvas.width = width; // this.canvas.height = height; this.clear(); ctx.fillStyle = '#fff'; // 设置背景色为白色 ctx.fillRect(0, 0, width, height); } clear() { const canvas = this.canvas; this.ctx.clearRect(0, 0, canvas.width, canvas.height); } } export default TextDrawService;