feat: 增加找字模板

This commit is contained in:
R524809
2025-11-20 17:35:37 +08:00
parent 2e0f8f0fe1
commit 654ea69915
11 changed files with 1061 additions and 532 deletions
+59 -75
View File
@@ -1,7 +1,10 @@
import { PAPER_SIZE } from '../constants/colors';
import { getMiniCodeImage, getImage } from '../utils/index';
import { ShapeCard } from '../constants/shapes';
import { drawShape } from './drawShape';
import {
drawHeader as drawHeaderCommon,
drawMiniHeader as drawMiniHeaderCommon,
} from './headerDrawService';
/**
* 计算图形在画布上的位置,避免重叠
@@ -36,8 +39,16 @@ function calculateShapePositions(
const col = i % cols;
// 在单元格内随机位置
const x = padding + col * cellWidth + (cellWidth - shapeSize) / 2 + (Math.random() - 0.5) * (cellWidth - shapeSize) * 0.3;
const y = padding + row * cellHeight + (cellHeight - shapeSize) / 2 + (Math.random() - 0.5) * (cellHeight - shapeSize) * 0.3;
const x =
padding +
col * cellWidth +
(cellWidth - shapeSize) / 2 +
(Math.random() - 0.5) * (cellWidth - shapeSize) * 0.3;
const y =
padding +
row * cellHeight +
(cellHeight - shapeSize) / 2 +
(Math.random() - 0.5) * (cellHeight - shapeSize) * 0.3;
positions.push({ x, y });
}
@@ -45,7 +56,6 @@ function calculateShapePositions(
return positions;
}
class ShapeDrawService {
headerType: PrintHeader;
canvas: WechatMiniprogram.Canvas;
@@ -102,74 +112,38 @@ class ShapeDrawService {
}
async drawHeader() {
const { canvas, ctx } = this;
const { appName, appHint, title, subTitle } = this.options;
this.currentX = 80;
this.currentY = 80;
let titleX = this.currentX + 200 + 48;
const titleY = 80;
const logoX = 80;
const logoY = 60;
const logoWidth = 200;
const logoHeight = 200;
switch (this.headerType) {
case 'LogoImage': {
const image = await getImage(canvas, '/assets/imgs/doodle-logo.png');
ctx.drawImage(image, logoX, logoY, logoWidth, logoHeight);
break;
}
case 'noLogoImage': {
titleX = 120;
break;
}
case 'minimal': {
titleX = 120;
break;
}
default: {
const image = await getMiniCodeImage(canvas);
ctx.drawImage(image, logoX, logoY, logoWidth, logoHeight);
break;
}
}
ctx.font = 'bold 64px "Microsoft Yahei"';
ctx.fillStyle = '#000';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText(appName, titleX, titleY);
ctx.font = '48px "Microsoft Yahei"';
ctx.fillStyle = '#666';
ctx.fillText(appHint, titleX, 186);
ctx.font = 'bold 64px "Microsoft Yahei"';
ctx.fillStyle = '#000';
ctx.fillText(title, 1015, titleY);
ctx.font = '48px "Microsoft Yahei"';
ctx.fillStyle = '#666';
ctx.fillText(subTitle, 1015, 186);
this.currentY = 304;
this.drawLine(this.currentY);
await drawHeaderCommon({
canvas: this.canvas,
ctx: this.ctx,
headerType: this.headerType,
options: {
appName: this.options.appName || '涂鸦丫小程序',
appHint: this.options.appHint || '涂色|识字|画画|打印',
title: this.options.title || '找一找 涂 色',
subTitle: this.options.subTitle || '给图形涂上相同的颜色',
},
onHeaderDrawn: (currentY) => {
this.currentY = currentY;
this.drawLine(this.currentY);
},
});
}
drawMiniHeader() {
const { canvas, ctx } = this;
const { appName, title } = this.options;
const titleY = 120;
ctx.font = 'bold 64px "Microsoft Yahei"';
ctx.fillStyle = '#000';
ctx.textAlign = 'center';
ctx.fillText(appName + ' ' + title, canvas.width / 2, titleY);
this.currentY = 200;
this.drawLine(this.currentY);
drawMiniHeaderCommon({
canvas: this.canvas,
ctx: this.ctx,
options: {
appName: this.options.appName || '涂鸦丫小程序',
title: this.options.title || '找一找 涂 色',
},
onHeaderDrawn: (currentY) => {
this.currentY = currentY;
this.drawLine(this.currentY);
},
});
}
drawLegend() {
@@ -245,7 +219,9 @@ class ShapeDrawService {
// 每行最多能放多少个图形(考虑最小间距40)
const minGap = 40;
const maxPerRow = Math.floor((contentWidth + minGap) / (shapeSize + minGap));
const maxPerRow = Math.floor(
(contentWidth + minGap) / (shapeSize + minGap),
);
// 总共要绘制多少个图形(每行都填满)
const totalShapes = maxPerRow * ROW_COUNT;
@@ -263,14 +239,20 @@ class ShapeDrawService {
}
// 生成所有图形的位置
let positions: { x: number, y: number }[] = [];
let positions: { x: number; y: number }[] = [];
for (let row = 0; row < ROW_COUNT; row++) {
// 本行起始Y
const y = contentTop + row * (shapeSize + VERTICAL_GAP) + shapeSize / 2;
const y =
contentTop + row * (shapeSize + VERTICAL_GAP) + shapeSize / 2;
// 本行实际间距
const gap = maxPerRow > 1
? Math.max(minGap, (contentWidth - maxPerRow * shapeSize) / (maxPerRow - 1))
: 0;
const gap =
maxPerRow > 1
? Math.max(
minGap,
(contentWidth - maxPerRow * shapeSize) /
(maxPerRow - 1),
)
: 0;
// 本行起始X(水平居中)
let startX = leftMargin;
if (maxPerRow > 1) {
@@ -282,7 +264,7 @@ class ShapeDrawService {
for (let col = 0; col < maxPerRow; col++) {
positions.push({
x: startX + col * (shapeSize + gap) + shapeSize / 2,
y: y
y: y,
});
}
}
@@ -297,7 +279,9 @@ class ShapeDrawService {
}
// 记录实际绘制的图形数量
console.log(`绘制图形:计划${totalShapes}个,实际${positions.length}个;每行${maxPerRow}个,共${ROW_COUNT}行;边距 左右=${leftMargin}/${rightMargin},顶部=${topGap}(基于legend底线)`);
console.log(
`绘制图形:计划${totalShapes}个,实际${positions.length}个;每行${maxPerRow}个,共${ROW_COUNT}行;边距 左右=${leftMargin}/${rightMargin},顶部=${topGap}(基于legend底线)`,
);
// 绘制所有图形
positions.forEach((pos, index) => {