Files
doodle-mini/miniprogram/canvasService/textDrawServiceV2.ts
T
2025-03-30 19:59:59 +08:00

240 lines
7.8 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 { PAPER_SIZE } from './constant';
import { getMiniCodeImage } from './utils';
class TextDrawService {
canvas: WechatMiniprogram.Canvas;
ctx: RenderingContext;
options: Record<string, any>;
paperSize: PaperSize;
currentX: number;
currentY: number;
constructor(canvas: Canvas, ctx: RenderingContext, options?: Record<string, any>) {
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 为 alphabeticy 对应字母基线(类似左下角)
*
* */
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;
console.log('width---:', width);
console.log('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;