feat:数字首页
@@ -2,10 +2,15 @@
|
|||||||
"pages": [
|
"pages": [
|
||||||
"pages/index/index",
|
"pages/index/index",
|
||||||
"pages/shape/index",
|
"pages/shape/index",
|
||||||
"pages/debug/debug",
|
"pages/mathIndex/mathIndex",
|
||||||
"pages/wordDemo/index",
|
|
||||||
"pages/copyBook/copyBook",
|
"pages/copyBook/copyBook",
|
||||||
"pages/buttonTest/buttonTest"
|
"pages/debug/debug"
|
||||||
|
],
|
||||||
|
"subPackages": [
|
||||||
|
{
|
||||||
|
"root": "mathPages",
|
||||||
|
"pages": ["numberColor/numberColor"]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"window": {},
|
"window": {},
|
||||||
"style": "v2",
|
"style": "v2",
|
||||||
@@ -37,6 +42,12 @@
|
|||||||
"selectedIconPath": "assets/tabBar/icon-word-active.png",
|
"selectedIconPath": "assets/tabBar/icon-word-active.png",
|
||||||
"text": "识字"
|
"text": "识字"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/mathIndex/mathIndex",
|
||||||
|
"iconPath": "assets/tabBar/icon-math.png",
|
||||||
|
"selectedIconPath": "assets/tabBar/icon-math-active.png",
|
||||||
|
"text": "数学"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"pagePath": "pages/copyBook/copyBook",
|
"pagePath": "pages/copyBook/copyBook",
|
||||||
"iconPath": "assets/tabBar/icon-edit.png",
|
"iconPath": "assets/tabBar/icon-edit.png",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
@@ -1,26 +0,0 @@
|
|||||||
const PAPER_SIZE = {
|
|
||||||
A4: {
|
|
||||||
// width: 2480,
|
|
||||||
// height: 3508,
|
|
||||||
width: 595,
|
|
||||||
height: 842,
|
|
||||||
},
|
|
||||||
A3: {
|
|
||||||
width: 3508,
|
|
||||||
height: 4960,
|
|
||||||
},
|
|
||||||
A2: {
|
|
||||||
width: 4960,
|
|
||||||
height: 7016,
|
|
||||||
},
|
|
||||||
A1: {
|
|
||||||
width: 7016,
|
|
||||||
height: 10032,
|
|
||||||
},
|
|
||||||
A0: {
|
|
||||||
width: 10032,
|
|
||||||
height: 14176,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export { PAPER_SIZE };
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
import { PAPER_SIZE } from './constant';
|
|
||||||
import { getMiniCodeImagePath } 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: 80,
|
|
||||||
colors: ['#2196F3', '#FFEB3B', '#FF9800', '#F44336'],
|
|
||||||
characters: ['鱼', '鸟', '猫', '狗'],
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
this.currentX = 0;
|
|
||||||
this.currentY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
draw() {
|
|
||||||
this.setPaper();
|
|
||||||
this.drawHeader();
|
|
||||||
this.drawLegend();
|
|
||||||
}
|
|
||||||
|
|
||||||
drawHeader() {
|
|
||||||
const { canvas, ctx } = this;
|
|
||||||
const { title } = this.options;
|
|
||||||
|
|
||||||
ctx.font = 'bold 80px "Microsoft Yahei"';
|
|
||||||
ctx.fillStyle = '#333';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
const titleX = canvas.width / 2;
|
|
||||||
const titleY = 120;
|
|
||||||
ctx.fillText(title, titleX, titleY);
|
|
||||||
|
|
||||||
// 记录当前X、Y坐标
|
|
||||||
this.currentX = titleX;
|
|
||||||
this.currentY = titleY + 50;
|
|
||||||
// 绘制分割线
|
|
||||||
this.drawLine(this.currentY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制示例
|
|
||||||
drawLegend() {
|
|
||||||
const { ctx, options } = this;
|
|
||||||
const { colors, characters, radius } = options;
|
|
||||||
const startX = (this.canvas.width - colors.length * 400) / 2; // 计算起始X坐标以居中
|
|
||||||
this.currentY = this.currentY + radius + 50; // radius 为圆的半径,再加上离线的边距为50
|
|
||||||
const startY = this.currentY; // 设置起始Y坐标
|
|
||||||
|
|
||||||
ctx.moveTo(startX, startY);
|
|
||||||
colors.forEach((color: string, index: number) => {
|
|
||||||
const x = startX + index * 500; // 计算每个圆的X坐标
|
|
||||||
const y = startY; // 计算圆的Y坐标
|
|
||||||
|
|
||||||
// 绘制圆
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
||||||
ctx.fillStyle = color;
|
|
||||||
ctx.fill();
|
|
||||||
ctx.fillStyle = '#333';
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.closePath();
|
|
||||||
|
|
||||||
// 绘制长方形
|
|
||||||
ctx.fillStyle = '#fff'; // 长方形背景色
|
|
||||||
ctx.strokeStyle = '#333';
|
|
||||||
ctx.lineWidth = 4;
|
|
||||||
const rectangleX = x - 120; // 计算字符X坐标
|
|
||||||
const rectangleY = y + radius + 40; // 计算字符Y坐标
|
|
||||||
ctx.strokeRect(rectangleX, rectangleY, 240, 100); // 绘制长方形
|
|
||||||
// ctx.fillRect(x - 75, y + 10, 240, 100); // 填充长方形
|
|
||||||
|
|
||||||
// 绘制字符
|
|
||||||
ctx.fillStyle = '#333';
|
|
||||||
ctx.font = 'bold 60px "Microsoft Yahei"';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillText(characters[index], rectangleX + 120, rectangleY + 70);
|
|
||||||
this.currentY = rectangleY + 100; // 100会矩形宽度
|
|
||||||
});
|
|
||||||
|
|
||||||
this.currentY = this.currentY + 50; // 计算分割线的Y坐标,距离示例图20px
|
|
||||||
this.drawLine(this.currentY);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawLine(linY: number) {
|
|
||||||
const { canvas, ctx } = this;
|
|
||||||
|
|
||||||
ctx.strokeStyle = '#333';
|
|
||||||
ctx.lineWidth = 4;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(20, linY);
|
|
||||||
ctx.lineTo(canvas.width - 20, linY);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
setPaper() {
|
|
||||||
const ctx = this.ctx;
|
|
||||||
const { width, height } = PAPER_SIZE[this.paperSize];
|
|
||||||
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;
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
// 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 为 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;
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
// import { PAPER_SIZE } from './constant';
|
|
||||||
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: '把相同的文字涂上相同的颜色',
|
|
||||||
colors: ['#FF0000', '#FDAF1A', '#0057FC', '#09FF00', '#8D00FF'],
|
|
||||||
characters: ['文', '升', '国', '舟', '丹'],
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
this.currentX = 0;
|
|
||||||
this.currentY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
draw() {
|
|
||||||
this.setPaper();
|
|
||||||
this.drawHeader();
|
|
||||||
this.drawLegend();
|
|
||||||
this.drawContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
async drawHeader() {
|
|
||||||
const { canvas, ctx } = this;
|
|
||||||
const { appName, appHint, title, subTitle } = this.options;
|
|
||||||
|
|
||||||
this.currentX = 80;
|
|
||||||
this.currentY = 80;
|
|
||||||
const titleX = this.currentX + 200 + 48;
|
|
||||||
const titleY = 80;
|
|
||||||
const image = await getMiniCodeImage(canvas);
|
|
||||||
ctx.drawImage(image, 80, 80, 200, 200);
|
|
||||||
|
|
||||||
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.strokeStyle = '#888';
|
|
||||||
// ctx.lineWidth = 2;
|
|
||||||
// ctx.beginPath();
|
|
||||||
// ctx.moveTo(483, 70);
|
|
||||||
// ctx.lineTo(483, 70 + 140);
|
|
||||||
// ctx.stroke();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 绘制示例
|
|
||||||
* 矩形: x、y为左上角
|
|
||||||
* 圆形:x、y为圆心
|
|
||||||
* 文字:x textAlign 为 start,文本左边缘对齐
|
|
||||||
* y textBaseline 为 alphabetic,y 对应字母基线(类似左下角)
|
|
||||||
*
|
|
||||||
* */
|
|
||||||
drawLegend() {
|
|
||||||
const { canvas, ctx, options } = this;
|
|
||||||
const { colors, characters } = options;
|
|
||||||
const radius = 65;
|
|
||||||
const rectWidth = 180;
|
|
||||||
const rectHeight = 80;
|
|
||||||
const startX = 260 + radius; // 圆形的X坐标是圆心
|
|
||||||
const startY = 304 + 26 + radius; // 圆形的Y坐标是圆心
|
|
||||||
const len = characters.length || 4;
|
|
||||||
const canvasWidth = canvas.width;
|
|
||||||
/** 计算每个圆之间的间距 俩个60,一个是页面右侧边距,另一个是圆离右边距地边距*/
|
|
||||||
const spaceWidth = (canvasWidth - startX * 2) / (len - 1);
|
|
||||||
|
|
||||||
ctx.moveTo(startX, startY);
|
|
||||||
colors.forEach((color: string, index: number) => {
|
|
||||||
const x = startX + index * spaceWidth;
|
|
||||||
const y = startY; // 计算圆的Y坐标
|
|
||||||
|
|
||||||
// 绘制圆
|
|
||||||
ctx.strokeStyle = '#000';
|
|
||||||
ctx.fillStyle = color;
|
|
||||||
ctx.lineWidth = 4;
|
|
||||||
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 = 4;
|
|
||||||
const rectangleX = x - rectWidth / 2; // 从圆形移动一半的长方形的长
|
|
||||||
const rectangleY = y + radius + 43; // 从圆形下移一个半径,再加上43的间距
|
|
||||||
ctx.strokeRect(rectangleX, rectangleY, rectWidth, rectHeight); // 绘制长方形
|
|
||||||
|
|
||||||
// 绘制字符
|
|
||||||
ctx.fillStyle = '#333';
|
|
||||||
ctx.font = 'bold 48px "Microsoft Yahei"';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillText(
|
|
||||||
characters[index],
|
|
||||||
rectangleX + rectWidth / 2,
|
|
||||||
rectangleY + rectHeight / 2,
|
|
||||||
rectWidth,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.currentY = 612; // 计算分割线的Y坐标,距离示例图20px
|
|
||||||
this.drawLine(this.currentY);
|
|
||||||
}
|
|
||||||
|
|
||||||
drawContent() {
|
|
||||||
const { canvas, ctx, options } = this;
|
|
||||||
const { characters } = options;
|
|
||||||
|
|
||||||
const len = characters.length;
|
|
||||||
const rows = 8;
|
|
||||||
const radius = 80;
|
|
||||||
const fontSize = 72;
|
|
||||||
|
|
||||||
const startY = this.currentY + 62 + radius;
|
|
||||||
const startX1 = 310 + radius;
|
|
||||||
const startX2 = 200 + radius;
|
|
||||||
const canvasWidth = canvas.width;
|
|
||||||
const spaceWidthFrist = (canvasWidth - startX1 * 2) / (5 - 1);
|
|
||||||
const spaceWidthSecond = (canvasWidth - startX2 * 2) / (6 - 1);
|
|
||||||
const spaceHeight = 54;
|
|
||||||
|
|
||||||
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 = 4;
|
|
||||||
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(linY: number) {
|
|
||||||
const { canvas, ctx } = this;
|
|
||||||
|
|
||||||
ctx.strokeStyle = '#000';
|
|
||||||
ctx.lineWidth = 2;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(80, linY);
|
|
||||||
ctx.lineTo(canvas.width - 80, linY);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
setPaper() {
|
|
||||||
const { ctx, canvas } = this;
|
|
||||||
const { pixelRatio: dpr } = wx.getWindowInfo();
|
|
||||||
// const { width, height } = { width: 595 * dpr, height: 842 * dpr };
|
|
||||||
let { width, height } = PAPER_SIZE[this.paperSize];
|
|
||||||
width = width * dpr;
|
|
||||||
height = height * dpr;
|
|
||||||
|
|
||||||
canvas.width = width;
|
|
||||||
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;
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { PAPER_SIZE } from './constant';
|
|
||||||
|
|
||||||
export function setCanvasSize(canvas: Canvas, paperSize: PaperSize) {
|
|
||||||
const sizeObj = PAPER_SIZE[paperSize];
|
|
||||||
// const { pixelRatio: dpr, windowWidth, windowHeight } = wx.getWindowInfo();
|
|
||||||
// const printWidth = A4.width;
|
|
||||||
// const printHeight = A4.height;
|
|
||||||
// @ts-ignore
|
|
||||||
// const originWidth = canvas._width || windowWidth;
|
|
||||||
// // @ts-ignore
|
|
||||||
// const originHeight = canvas._height || windowHeight;
|
|
||||||
|
|
||||||
canvas.width = sizeObj.width;
|
|
||||||
canvas.height = sizeObj.height;
|
|
||||||
console.log('canvas----:', canvas);
|
|
||||||
return canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getMiniCodeImage(canvas: Canvas) {
|
|
||||||
return getImage(canvas, '/assets/imgs/doodle-mini-code.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getImage(canvas: Canvas, path: string) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const image = canvas.createImage();
|
|
||||||
image.onload = () => {
|
|
||||||
resolve(image);
|
|
||||||
};
|
|
||||||
image.onerror = () => {
|
|
||||||
reject('图片加载异常');
|
|
||||||
};
|
|
||||||
image.src = path;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"navigationBarTitleText": "形状涂色练习生成器",
|
|
||||||
"navigationBarBackgroundColor": "#667eea",
|
|
||||||
"homeButton": true,
|
|
||||||
"backgroundColor": "#f5f5f5",
|
|
||||||
"enablePullDownRefresh": false,
|
|
||||||
"usingComponents": {
|
|
||||||
"van-popup": "@vant/weapp/popup/index",
|
|
||||||
"van-icon": "@vant/weapp/icon/index",
|
|
||||||
"color-picker": "/components/color-picker/color-picker"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,537 +0,0 @@
|
|||||||
.container {
|
|
||||||
padding: 20rpx;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 30rpx;
|
|
||||||
padding: 30rpx;
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
border-radius: 25rpx;
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.15);
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 40rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #666;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
border-radius: 25rpx;
|
|
||||||
padding: 30rpx;
|
|
||||||
margin-bottom: 25rpx;
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.15);
|
|
||||||
backdrop-filter: blur(10rpx);
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 25rpx;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
|
||||||
padding-bottom: 15rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 选择形状按钮 */
|
|
||||||
.select-shapes-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 90rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(102, 126, 234, 0.4);
|
|
||||||
|
|
||||||
.btn-text {
|
|
||||||
margin-left: 10rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 形状卡片容器 */
|
|
||||||
.shape-cards-container {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 形状卡片 */
|
|
||||||
.shape-card {
|
|
||||||
position: relative;
|
|
||||||
padding: 20rpx;
|
|
||||||
background: linear-gradient(145deg, #ffffff, #f0f0f0);
|
|
||||||
border-radius: 20rpx;
|
|
||||||
border: 2rpx solid #e0e0e0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
transform: scale(0.98);
|
|
||||||
background: linear-gradient(145deg, #f8f9fa, #e9ecef);
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 10rpx;
|
|
||||||
right: 10rpx;
|
|
||||||
width: 40rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-preview-container {
|
|
||||||
position: relative;
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
margin-bottom: 15rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-preview-canvas {
|
|
||||||
position: absolute;
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
opacity: 0;
|
|
||||||
/* 隐藏,只用于未来可能的Canvas预览 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-name {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 10rpx;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8rpx;
|
|
||||||
|
|
||||||
.color-dot {
|
|
||||||
width: 20rpx;
|
|
||||||
height: 20rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2rpx solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-text {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 形状选择弹窗样式 */
|
|
||||||
.shape-selector-popup {
|
|
||||||
.van-popup {
|
|
||||||
max-height: 80vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-selector-container {
|
|
||||||
padding: 30rpx;
|
|
||||||
max-height: 80vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
.popup-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 30rpx;
|
|
||||||
padding-bottom: 20rpx;
|
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
|
||||||
|
|
||||||
.popup-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-count {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #667eea;
|
|
||||||
background: rgba(102, 126, 234, 0.1);
|
|
||||||
padding: 8rpx 15rpx;
|
|
||||||
border-radius: 15rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 20rpx;
|
|
||||||
margin-bottom: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-actions {
|
|
||||||
padding-top: 20rpx;
|
|
||||||
border-top: 2rpx solid #f0f0f0;
|
|
||||||
|
|
||||||
.confirm-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 80rpx;
|
|
||||||
font-size: 30rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 弹窗中的形状选项 */
|
|
||||||
.shape-option {
|
|
||||||
position: relative;
|
|
||||||
height: 140rpx;
|
|
||||||
border: 3rpx solid #e0e0e0;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: linear-gradient(145deg, #ffffff, #f0f0f0);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
border-color: #667eea;
|
|
||||||
background: linear-gradient(145deg, #e3f2fd, #bbdefb);
|
|
||||||
transform: translateY(-5rpx);
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(102, 126, 234, 0.3);
|
|
||||||
|
|
||||||
.shape-name {
|
|
||||||
color: #667eea;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-color: #ddd;
|
|
||||||
|
|
||||||
.shape-name {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-mark {
|
|
||||||
position: absolute;
|
|
||||||
top: 8rpx;
|
|
||||||
right: 8rpx;
|
|
||||||
width: 24rpx;
|
|
||||||
height: 24rpx;
|
|
||||||
background: #667eea;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-preview-box {
|
|
||||||
width: 60rpx;
|
|
||||||
height: 60rpx;
|
|
||||||
margin-bottom: 10rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SVG 样式 */
|
|
||||||
.shape-svg {
|
|
||||||
width: 60rpx;
|
|
||||||
height: 60rpx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-svg-small {
|
|
||||||
width: 50rpx;
|
|
||||||
height: 50rpx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 保留基础样式,移除所有形状相关的CSS */
|
|
||||||
|
|
||||||
.shape-name {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #666;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 颜色选择器 */
|
|
||||||
.color-selector {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(120rpx, 1fr));
|
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-option {
|
|
||||||
height: 100rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
border: 3rpx solid #e0e0e0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
position: relative;
|
|
||||||
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
border-color: #667eea;
|
|
||||||
transform: scale(1.05);
|
|
||||||
box-shadow: 0 0 0 6rpx rgba(102, 126, 234, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-name {
|
|
||||||
color: white;
|
|
||||||
font-size: 22rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
text-shadow: 1rpx 1rpx 2rpx rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 添加按钮 */
|
|
||||||
.add-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 90rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(102, 126, 234, 0.4);
|
|
||||||
|
|
||||||
.add-icon {
|
|
||||||
font-size: 40rpx;
|
|
||||||
margin-right: 10rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 已选择的形状 */
|
|
||||||
.selected-shapes {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 15rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-shape {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20rpx;
|
|
||||||
background: linear-gradient(145deg, #f8f9fa, #e9ecef);
|
|
||||||
border-radius: 20rpx;
|
|
||||||
border: 2rpx solid #e0e0e0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: linear-gradient(145deg, #ffebee, #ffcdd2);
|
|
||||||
border-color: #ff5722;
|
|
||||||
transform: scale(0.98);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-preview {
|
|
||||||
width: 40rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 20rpx;
|
|
||||||
border: 2rpx solid #333;
|
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-details {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shape-info {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-info {
|
|
||||||
font-size: 22rpx;
|
|
||||||
color: #666;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove-hint {
|
|
||||||
font-size: 20rpx;
|
|
||||||
color: #999;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 设置项 */
|
|
||||||
.setting-item {
|
|
||||||
margin-bottom: 30rpx;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-label {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 15rpx;
|
|
||||||
display: block;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-radio-group {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 25rpx;
|
|
||||||
margin-top: 15rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-option {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10rpx;
|
|
||||||
padding: 10rpx 15rpx;
|
|
||||||
border-radius: 15rpx;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
text {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-intro {
|
|
||||||
margin-top: 30rpx;
|
|
||||||
padding: 25rpx;
|
|
||||||
background: linear-gradient(145deg, #f0f8ff, #e6f3ff);
|
|
||||||
border-radius: 15rpx;
|
|
||||||
border-left: 5rpx solid #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro-title {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 15rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.intro-text {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #666;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
padding-left: 15rpx;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 操作按钮 */
|
|
||||||
.generate-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 90rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
background: linear-gradient(45deg, #4CAF50, #45a049);
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(76, 175, 80, 0.4);
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
background: #ccc;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.save-btn {
|
|
||||||
width: 100%;
|
|
||||||
height: 90rpx;
|
|
||||||
font-size: 32rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
background: linear-gradient(45deg, #ff9800, #f57c00);
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(255, 152, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 隐藏的画布 */
|
|
||||||
.hidden-canvas {
|
|
||||||
position: fixed;
|
|
||||||
top: -9999rpx;
|
|
||||||
left: -9999rpx;
|
|
||||||
width: 595px;
|
|
||||||
height: 842px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 预览图片 */
|
|
||||||
.preview-image {
|
|
||||||
width: 100%;
|
|
||||||
max-height: 600rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
border: 3rpx solid #e0e0e0;
|
|
||||||
background-color: white;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.15);
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
transform: scale(0.98);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-hint {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #666;
|
|
||||||
margin-top: 20rpx;
|
|
||||||
padding: 15rpx;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
border-radius: 15rpx;
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
<view class="container">
|
|
||||||
<!-- 标题 -->
|
|
||||||
<view class="header">
|
|
||||||
<text class="title">形状涂色练习生成器</text>
|
|
||||||
<text class="subtitle">为小朋友制作涂色练习页</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 选择形状按钮 -->
|
|
||||||
<view class="section">
|
|
||||||
<button
|
|
||||||
class="select-shapes-btn"
|
|
||||||
bindtap="openShapeSelector"
|
|
||||||
type="primary">
|
|
||||||
<van-icon name="add-o" color="white" size="20" />
|
|
||||||
<text class="btn-text">选择形状 ({{selectedShapes.length}}/6)</text>
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 已选择的形状卡片 -->
|
|
||||||
<view class="section" wx:if="{{selectedShapes.length > 0}}">
|
|
||||||
<view class="section-title"
|
|
||||||
>已选择的形状 ({{selectedShapes.length}})</view
|
|
||||||
>
|
|
||||||
<view class="shape-cards-container">
|
|
||||||
<view
|
|
||||||
wx:for="{{selectedShapes}}"
|
|
||||||
wx:key="id"
|
|
||||||
class="shape-card"
|
|
||||||
bindtap="openColorPicker"
|
|
||||||
data-index="{{index}}">
|
|
||||||
<!-- 删除按钮 -->
|
|
||||||
<view
|
|
||||||
class="delete-btn"
|
|
||||||
catchtap="deleteShape"
|
|
||||||
data-index="{{index}}">
|
|
||||||
<van-icon name="close" color="#ff4757" size="16" />
|
|
||||||
</view>
|
|
||||||
<!-- 形状预览 -->
|
|
||||||
<view class="shape-preview-container">
|
|
||||||
<image
|
|
||||||
src="{{item.svgContent}}"
|
|
||||||
class="shape-svg"
|
|
||||||
mode="aspectFit">
|
|
||||||
</image>
|
|
||||||
</view>
|
|
||||||
<!-- 形状名称 -->
|
|
||||||
<text class="shape-name">{{item.name}}</text>
|
|
||||||
<!-- 颜色信息 -->
|
|
||||||
<view class="color-info">
|
|
||||||
<view
|
|
||||||
class="color-dot"
|
|
||||||
style="background-color: {{item.fillColor}};"></view>
|
|
||||||
<text class="color-text">{{item.colorName}}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 设置区域 -->
|
|
||||||
<view class="section" wx:if="{{selectedShapes.length > 0}}">
|
|
||||||
<view class="section-title">打印设置</view>
|
|
||||||
<view class="setting-item">
|
|
||||||
<text class="setting-label">背景颜色:</text>
|
|
||||||
<radio-group bindchange="setBackground" class="bg-radio-group">
|
|
||||||
<label
|
|
||||||
wx:for="{{backgrounds}}"
|
|
||||||
wx:key="value"
|
|
||||||
class="bg-option">
|
|
||||||
<radio
|
|
||||||
value="{{item.value}}"
|
|
||||||
checked="{{bgColor === item.value}}"
|
|
||||||
color="#1976d2" />
|
|
||||||
<text>{{item.name}}</text>
|
|
||||||
</label>
|
|
||||||
</radio-group>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 生成按钮 -->
|
|
||||||
<view class="section" wx:if="{{selectedShapes.length > 0}}">
|
|
||||||
<button
|
|
||||||
class="generate-btn"
|
|
||||||
bindtap="generateImage"
|
|
||||||
type="primary"
|
|
||||||
loading="{{isGenerating}}"
|
|
||||||
disabled="{{selectedShapes.length === 0}}">
|
|
||||||
{{isGenerating ? '生成中...' : '生成练习页'}}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
wx:if="{{generatedImage}}"
|
|
||||||
class="save-btn"
|
|
||||||
bindtap="saveToAlbum"
|
|
||||||
type="warn">
|
|
||||||
保存到相册
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 预览生成的图片 -->
|
|
||||||
<view class="section" wx:if="{{generatedImage}}">
|
|
||||||
<view class="section-title">生成的练习页</view>
|
|
||||||
<image
|
|
||||||
src="{{generatedImage}}"
|
|
||||||
class="preview-image"
|
|
||||||
mode="aspectFit"
|
|
||||||
bindtap="previewImage" />
|
|
||||||
<text class="preview-hint">点击图片可放大预览 • A4尺寸 • 适合打印</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 形状选择弹窗 -->
|
|
||||||
<van-popup
|
|
||||||
show="{{showShapeSelector}}"
|
|
||||||
bind:close="closeShapeSelector"
|
|
||||||
custom-class="shape-selector-popup"
|
|
||||||
position="bottom"
|
|
||||||
round>
|
|
||||||
<view class="shape-selector-container">
|
|
||||||
<view class="popup-header">
|
|
||||||
<text class="popup-title">选择形状 (最多6个)</text>
|
|
||||||
<view class="selected-count"
|
|
||||||
>已选: {{selectedShapes.length}}/6</view
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="shape-grid">
|
|
||||||
<view
|
|
||||||
wx:for="{{availableShapes}}"
|
|
||||||
wx:key="id"
|
|
||||||
class="shape-option {{tempSelectedShapeIds.indexOf(item.id) !== -1 ? 'selected' : ''}} {{selectedShapes.length >= 6 && tempSelectedShapeIds.indexOf(item.id) === -1 ? 'disabled' : ''}}"
|
|
||||||
bindtap="toggleShape"
|
|
||||||
data-shape-id="{{item.id}}">
|
|
||||||
<!-- 选中标识 -->
|
|
||||||
<view
|
|
||||||
class="selected-mark"
|
|
||||||
wx:if="{{tempSelectedShapeIds.indexOf(item.id) !== -1}}">
|
|
||||||
<van-icon name="success" color="white" size="12" />
|
|
||||||
</view>
|
|
||||||
<!-- 形状预览 -->
|
|
||||||
<view class="shape-preview-box">
|
|
||||||
<image
|
|
||||||
src="{{item.svgContent}}"
|
|
||||||
class="shape-svg-small"
|
|
||||||
mode="aspectFit">
|
|
||||||
</image>
|
|
||||||
</view>
|
|
||||||
<text class="shape-name">{{item.name}}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="popup-actions">
|
|
||||||
<button
|
|
||||||
class="confirm-btn"
|
|
||||||
bindtap="confirmShapeSelection"
|
|
||||||
type="primary">
|
|
||||||
确认选择
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</van-popup>
|
|
||||||
|
|
||||||
<!-- 颜色选择器 -->
|
|
||||||
<color-picker
|
|
||||||
show="{{showColorPicker}}"
|
|
||||||
currentColor="{{currentEditingColor}}"
|
|
||||||
bind:onClose="closeColorPicker"
|
|
||||||
bind:onChange="onColorChange">
|
|
||||||
</color-picker>
|
|
||||||
|
|
||||||
<!-- Canvas 画布 (隐藏) -->
|
|
||||||
<canvas type="2d" id="mainCanvas" class="hidden-canvas"></canvas>
|
|
||||||
</view>
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
// shapes.js - 幼儿园到小学三年级基本几何形状定义
|
|
||||||
const shapes = [
|
|
||||||
{
|
|
||||||
id: 'circle',
|
|
||||||
name: '圆形',
|
|
||||||
type: 'circle',
|
|
||||||
svg: '<circle cx="50" cy="50" r="40" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawCircle',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'square',
|
|
||||||
name: '正方形',
|
|
||||||
type: 'rect',
|
|
||||||
svg: '<rect x="10" y="10" width="80" height="80" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawSquare',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rectangle',
|
|
||||||
name: '长方形',
|
|
||||||
type: 'rect',
|
|
||||||
svg: '<rect x="5" y="25" width="90" height="50" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawRectangle',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'triangle',
|
|
||||||
name: '三角形',
|
|
||||||
type: 'triangle',
|
|
||||||
svg: '<polygon points="50,10 20,80 80,80" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawTriangle',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'oval',
|
|
||||||
name: '椭圆形',
|
|
||||||
type: 'oval',
|
|
||||||
svg: '<ellipse cx="50" cy="50" rx="40" ry="25" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawOval',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'diamond',
|
|
||||||
name: '菱形',
|
|
||||||
type: 'diamond',
|
|
||||||
svg: '<polygon points="50,10 80,50 50,90 20,50" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawDiamond',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'star',
|
|
||||||
name: '星形',
|
|
||||||
type: 'star',
|
|
||||||
svg: '<polygon points="50,5 60,35 90,35 70,55 80,85 50,70 20,85 30,55 10,35 40,35" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawStar',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'heart',
|
|
||||||
name: '心形',
|
|
||||||
type: 'heart',
|
|
||||||
svg: '<path d="M50,85 C30,65 10,45 10,25 C10,15 20,5 30,5 C40,5 50,15 50,25 C50,15 60,5 70,5 C80,5 90,15 90,25 C90,45 70,65 50,85 Z" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawHeart',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'hexagon',
|
|
||||||
name: '六边形',
|
|
||||||
type: 'hexagon',
|
|
||||||
svg: '<polygon points="25,15 75,15 90,50 75,85 25,85 10,50" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawHexagon',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'pentagon',
|
|
||||||
name: '五边形',
|
|
||||||
type: 'pentagon',
|
|
||||||
svg: '<polygon points="50,10 80,35 65,75 35,75 20,35" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawPentagon',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'semicircle',
|
|
||||||
name: '半圆形',
|
|
||||||
type: 'semicircle',
|
|
||||||
svg: '<path d="M 15 50 A 35 35 0 0 1 85 50 Z" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawSemicircle',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'trapezoid',
|
|
||||||
name: '梯形',
|
|
||||||
type: 'trapezoid',
|
|
||||||
svg: '<polygon points="30,20 70,20 85,80 15,80" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawTrapezoid',
|
|
||||||
},
|
|
||||||
// 新增3D形状
|
|
||||||
{
|
|
||||||
id: 'cylinder',
|
|
||||||
name: '圆柱体',
|
|
||||||
type: '3d-cylinder',
|
|
||||||
svg: '<ellipse cx="50" cy="20" rx="30" ry="8" stroke="#333" stroke-width="2" fill="none"/><rect x="20" y="20" width="60" height="50" stroke="none" fill="none"/><line x1="20" y1="20" x2="20" y2="70" stroke="#333" stroke-width="2"/><line x1="80" y1="20" x2="80" y2="70" stroke="#333" stroke-width="2"/><ellipse cx="50" cy="70" rx="30" ry="8" stroke="#333" stroke-width="2" fill="none"/>',
|
|
||||||
drawFunction: 'drawCylinder',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'cube',
|
|
||||||
name: '正方体',
|
|
||||||
type: '3d-cube',
|
|
||||||
svg: '<polygon points="20,30 60,30 60,70 20,70" stroke="#333" stroke-width="2" fill="none"/><polygon points="30,20 70,20 70,60 30,60" stroke="#333" stroke-width="2" fill="none"/><line x1="20" y1="30" x2="30" y2="20" stroke="#333" stroke-width="2"/><line x1="60" y1="30" x2="70" y2="20" stroke="#333" stroke-width="2"/><line x1="60" y1="70" x2="70" y2="60" stroke="#333" stroke-width="2"/>',
|
|
||||||
drawFunction: 'drawCube',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'sphere',
|
|
||||||
name: '球体',
|
|
||||||
type: '3d-sphere',
|
|
||||||
svg: '<circle cx="50" cy="50" r="35" stroke="#333" stroke-width="2" fill="none"/><ellipse cx="50" cy="50" rx="35" ry="15" stroke="#333" stroke-width="1" fill="none" opacity="0.5"/><ellipse cx="50" cy="50" rx="15" ry="35" stroke="#333" stroke-width="1" fill="none" opacity="0.5"/>',
|
|
||||||
drawFunction: 'drawSphere',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'cone',
|
|
||||||
name: '圆锥体',
|
|
||||||
type: '3d-cone',
|
|
||||||
svg: '<ellipse cx="50" cy="80" rx="30" ry="8" stroke="#333" stroke-width="2" fill="none"/><line x1="20" y1="80" x2="50" y2="20" stroke="#333" stroke-width="2"/><line x1="80" y1="80" x2="50" y2="20" stroke="#333" stroke-width="2"/>',
|
|
||||||
drawFunction: 'drawCone',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// 颜色选项
|
|
||||||
const colors = [
|
|
||||||
{ name: '红色', value: '#FF4444' },
|
|
||||||
{ name: '蓝色', value: '#4A90E2' },
|
|
||||||
{ name: '绿色', value: '#7ED321' },
|
|
||||||
{ name: '黄色', value: '#F5A623' },
|
|
||||||
{ name: '紫色', value: '#9013FE' },
|
|
||||||
{ name: '橙色', value: '#FF8F00' },
|
|
||||||
{ name: '粉色', value: '#E91E63' },
|
|
||||||
{ name: '青色', value: '#00BCD4' },
|
|
||||||
{ name: '深绿', value: '#4CAF50' },
|
|
||||||
{ name: '深蓝', value: '#2196F3' },
|
|
||||||
{ name: '棕色', value: '#8D6E63' },
|
|
||||||
{ name: '灰色', value: '#9E9E9E' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export { shapes, colors };
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
Page({
|
|
||||||
data: {
|
|
||||||
showModal: false,
|
|
||||||
texts: ['', '', '', ''],
|
|
||||||
},
|
|
||||||
|
|
||||||
onReady: function () {
|
|
||||||
this.drawCircles();
|
|
||||||
},
|
|
||||||
|
|
||||||
drawCircles: function () {
|
|
||||||
const ctx = wx.createCanvasContext('myCanvas');
|
|
||||||
const { texts } = this.data;
|
|
||||||
|
|
||||||
// 绘制4个圆圈
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
const x = 50 + i * 100;
|
|
||||||
const y = 50;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(x, y, 40, 0, 2 * Math.PI);
|
|
||||||
ctx.setFillStyle('white');
|
|
||||||
ctx.fill();
|
|
||||||
ctx.setStrokeStyle('black');
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
if (texts[i]) {
|
|
||||||
ctx.setFillStyle('black');
|
|
||||||
ctx.setFontSize(20);
|
|
||||||
ctx.setTextAlign('center');
|
|
||||||
ctx.setTextBaseline('middle');
|
|
||||||
ctx.fillText(texts[i], x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.draw();
|
|
||||||
},
|
|
||||||
|
|
||||||
showInput: function () {
|
|
||||||
this.setData({ showModal: true });
|
|
||||||
},
|
|
||||||
|
|
||||||
updateText: function (e) {
|
|
||||||
const index = e.currentTarget.dataset.index;
|
|
||||||
const value = e.detail.value;
|
|
||||||
const { texts } = this.data;
|
|
||||||
texts[index] = value;
|
|
||||||
this.setData({ texts });
|
|
||||||
},
|
|
||||||
|
|
||||||
confirmInput: function () {
|
|
||||||
this.setData({ showModal: false });
|
|
||||||
this.drawCircles();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"navigationBarTitleText": "文本绘制 示例",
|
|
||||||
"navigationBarBackgroundColor": "#d2e7d8",
|
|
||||||
"homeButton": true,
|
|
||||||
"backgroundColor": "#e8eddb",
|
|
||||||
"enablePullDownRefresh": false
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
.page-container {
|
|
||||||
height: 100vh;
|
|
||||||
background-color: transparent;
|
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
.text-canvas {
|
|
||||||
margin: 20rpx 20rpx 60rpx;
|
|
||||||
width: calc(100% - 40rpx);
|
|
||||||
height: calc(100% - 80rpx);
|
|
||||||
border: 1rpx solid #333;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
Page({
|
|
||||||
canvas: null as WechatMiniprogram.Canvas | null,
|
|
||||||
ctx: null as CanvasRenderingContext2D | null,
|
|
||||||
|
|
||||||
data: {
|
|
||||||
showModal: false,
|
|
||||||
texts: ['', '', '', ''],
|
|
||||||
},
|
|
||||||
|
|
||||||
onReady: function () {
|
|
||||||
this.initCanvas();
|
|
||||||
},
|
|
||||||
|
|
||||||
initCanvas() {
|
|
||||||
const query = wx.createSelectorQuery();
|
|
||||||
query
|
|
||||||
.select('#textCanvas')
|
|
||||||
.fields({ node: true, size: true })
|
|
||||||
.exec((res) => {
|
|
||||||
const canvas = res[0].node;
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
this.canvas = canvas;
|
|
||||||
this.ctx = ctx;
|
|
||||||
const dpi = 300; // 明确声明 DPI
|
|
||||||
const mmToInch = 25.4;
|
|
||||||
const width = (210 / mmToInch) * dpi; // 2480px
|
|
||||||
const height = (297 / mmToInch) * dpi; // 3508px
|
|
||||||
const pixelRatio = wx.getWindowInfo().pixelRatio;
|
|
||||||
|
|
||||||
// 设置画布实际像素为 A4 尺寸
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
|
|
||||||
// 适配屏幕显示(非必须,仅用于预览)
|
|
||||||
canvas.style = canvas.style || {};
|
|
||||||
canvas.style.width = `${width / pixelRatio}px`;
|
|
||||||
canvas.style.height = `${height / pixelRatio}px`;
|
|
||||||
|
|
||||||
// 缩放上下文以适配高清屏
|
|
||||||
ctx.scale(pixelRatio, pixelRatio);
|
|
||||||
this.drawCircles();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setCanvasSize() { },
|
|
||||||
|
|
||||||
drawCircles: function () {
|
|
||||||
if (!this.canvas) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ctx = this.ctx as CanvasRenderingContext2D;
|
|
||||||
const { texts } = this.data;
|
|
||||||
|
|
||||||
// 清空画布
|
|
||||||
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
||||||
|
|
||||||
// 绘制4个圆圈
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
const x = 50 + i * 100;
|
|
||||||
const y = 50;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(x, y, 40, 0, 2 * Math.PI);
|
|
||||||
ctx.fillStyle = 'white';
|
|
||||||
ctx.fill();
|
|
||||||
ctx.strokeStyle = 'black';
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
if (texts[i]) {
|
|
||||||
ctx.fillStyle = 'black';
|
|
||||||
ctx.font = '20px sans-serif';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
ctx.fillText(texts[i], x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
showInput: function () {
|
|
||||||
this.setData({ showModal: true });
|
|
||||||
},
|
|
||||||
|
|
||||||
updateText: function (e: WechatMiniprogram.CustomEvent) {
|
|
||||||
const index = e.currentTarget.dataset.index;
|
|
||||||
const value = e.detail.value;
|
|
||||||
const { texts } = this.data;
|
|
||||||
texts[index] = value;
|
|
||||||
this.setData({ texts });
|
|
||||||
},
|
|
||||||
|
|
||||||
confirmInput: function () {
|
|
||||||
this.setData({ showModal: false });
|
|
||||||
this.drawCircles();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<view class="container">
|
|
||||||
<canvas canvas-id="myCanvas" style="width: 100%; height: 100vh;"></canvas>
|
|
||||||
<button class="input-button" bindtap="showInput">输入文本</button>
|
|
||||||
<view wx:if="{{showModal}}" class="modal">
|
|
||||||
<view class="modal-content">
|
|
||||||
<input placeholder="输入第一个文本" bindinput="updateText" data-index="0" />
|
|
||||||
<input placeholder="输入第二个文本" bindinput="updateText" data-index="1" />
|
|
||||||
<input placeholder="输入第三个文本" bindinput="updateText" data-index="2" />
|
|
||||||
<input placeholder="输入第四个文本" bindinput="updateText" data-index="3" />
|
|
||||||
<button bindtap="confirmInput">确认</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-button {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
background-color: white;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"navigationBarTitleText": "文本绘制 示例",
|
|
||||||
"navigationBarBackgroundColor": "#798154",
|
|
||||||
"homeButton": false,
|
|
||||||
"backgroundColorContent": "#ffffff00",
|
|
||||||
"enablePullDownRefresh": false
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// .color-picker {
|
|
||||||
// display: flex;
|
|
||||||
// justify-content: center;
|
|
||||||
// padding: 20rpx;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .color-circle {
|
|
||||||
// width: 80rpx;
|
|
||||||
// height: 80rpx;
|
|
||||||
// border-radius: 50%;
|
|
||||||
// margin: 0 40rpx;
|
|
||||||
// border: 4rpx solid #333;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .action-btn {
|
|
||||||
// width: 60%;
|
|
||||||
// background: #4CAF50;
|
|
||||||
// color: white;
|
|
||||||
// margin: 40rpx auto;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.container {
|
|
||||||
height: 100vh;
|
|
||||||
background-color: transparent;
|
|
||||||
background-color: transparent;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.canvas-container {
|
|
||||||
margin-top: 20rpx;
|
|
||||||
// margin: 20rpx;
|
|
||||||
// width: calc(100% - 40rpx);
|
|
||||||
// // width: 100%;
|
|
||||||
// height: 580px;
|
|
||||||
// height: calc(100% - 80rpx);
|
|
||||||
border: 2rpx solid #333;
|
|
||||||
background-color: #fff;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
// import TextDrawService from '../../canvasService/textDrawService';
|
|
||||||
// import TextDrawServiceV2 from '../../canvasService/textDrawServiceV2';
|
|
||||||
import { PAPER_SIZE } from '../../canvasService/constant';
|
|
||||||
import TextDrawServiceV3 from '../../canvasService/textDrawServiceV3';
|
|
||||||
|
|
||||||
Page({
|
|
||||||
canvas: null as Canvas | null,
|
|
||||||
ctx: null as RenderingContext | null,
|
|
||||||
data: {
|
|
||||||
characters: ['鱼', '鸟', '牛', '羊'] as const, // 固定汉字类型
|
|
||||||
colorMap: {
|
|
||||||
鱼: '#2196F3', // 蓝色
|
|
||||||
鸟: '#FFEB3B', // 黄色
|
|
||||||
牛: '#FF9800', // 橙色
|
|
||||||
羊: '#9C27B0', // 紫色
|
|
||||||
} as Record<string, string>,
|
|
||||||
matrix: [] as string[], // 随机汉字矩阵
|
|
||||||
selectedColor: '', // 当前选中颜色
|
|
||||||
selectedChar: '', // 当前选中汉字
|
|
||||||
},
|
|
||||||
|
|
||||||
onLoad() {
|
|
||||||
console.log('textPaint2 onLoad');
|
|
||||||
// this.setCanvasBoxSize();
|
|
||||||
// this.generateMatrix();
|
|
||||||
},
|
|
||||||
|
|
||||||
onReady() {
|
|
||||||
wx.nextTick(() => {
|
|
||||||
this.setCanvasBoxSize();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// setCanvasBoxSize() {
|
|
||||||
// const { width: paperWidth, height: paperHeight } = PAPER_SIZE['A4'];
|
|
||||||
|
|
||||||
// // 逻辑尺寸(容器宽高)
|
|
||||||
// const boxWidth = windowWidth - 40;
|
|
||||||
// const boxHeight = boxWidth / (paperWidth / paperHeight);
|
|
||||||
// console.log('boxWidth---:', boxWidth);
|
|
||||||
// console.log('boxHeight---:', boxHeight);
|
|
||||||
// this.setData({ boxWidth, boxHeight });
|
|
||||||
// // // 实际像素尺寸(需乘以设备像素比)
|
|
||||||
// // const canvasWidth = boxWidth * pixelRatio;
|
|
||||||
// // const canvasHeight = boxHeight * pixelRatio;
|
|
||||||
|
|
||||||
// // // 更新 Canvas 节点属性
|
|
||||||
// // const query = wx.createSelectorQuery().select('#textCanvas');
|
|
||||||
// // query.fields({ node: true, size: true }).exec((res) => {
|
|
||||||
// // const canvas = res[0].node;
|
|
||||||
// // canvas.width = canvasWidth;
|
|
||||||
// // canvas.height = canvasHeight;
|
|
||||||
// // // 缩放上下文以适配逻辑尺寸
|
|
||||||
// // const ctx = canvas.getContext('2d');
|
|
||||||
// // ctx.scale(pixelRatio, pixelRatio);
|
|
||||||
|
|
||||||
// // this.canvas = canvas;
|
|
||||||
// // this.ctx = ctx;
|
|
||||||
|
|
||||||
// // const textDrawService = new TextDrawServiceV3(canvas, ctx);
|
|
||||||
// // textDrawService.draw();
|
|
||||||
// // ctx.fillStyle = '#fff'; // 设置背景色为白色
|
|
||||||
// // ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
|
||||||
// // });
|
|
||||||
// },
|
|
||||||
|
|
||||||
// 生成6列随机矩阵
|
|
||||||
generateMatrix() {
|
|
||||||
const shuffled = [...this.data.characters]
|
|
||||||
.sort(() => Math.random() - 0.5)
|
|
||||||
.concat(
|
|
||||||
...Array(42)
|
|
||||||
.fill(null)
|
|
||||||
.map(() => this.data.characters[Math.floor(Math.random() * 4)]),
|
|
||||||
);
|
|
||||||
this.setData({ matrix: shuffled.slice(0, 42) }); // 6列x7行
|
|
||||||
},
|
|
||||||
|
|
||||||
setCanvasBoxSize() {
|
|
||||||
const { width, height } = PAPER_SIZE['A4'];
|
|
||||||
const { windowWidth } = wx.getWindowInfo();
|
|
||||||
const boxWidth = windowWidth - 20;
|
|
||||||
const boxHeight = boxWidth / (width / height);
|
|
||||||
console.log('boxWidth----:', boxWidth);
|
|
||||||
console.log('boxHeight----:', boxHeight);
|
|
||||||
this.setData({ boxHeight, boxWidth }, () => {
|
|
||||||
this.initCanvas();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 初始化A4画布(300DPI)
|
|
||||||
initCanvas() {
|
|
||||||
wx.createSelectorQuery()
|
|
||||||
.select('#textCanvas')
|
|
||||||
.fields({
|
|
||||||
node: true,
|
|
||||||
size: true,
|
|
||||||
})
|
|
||||||
.exec((res) => {
|
|
||||||
if (res[0] && res[0].node) {
|
|
||||||
const canvas = res[0].node;
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
if (ctx) {
|
|
||||||
this.canvas = canvas;
|
|
||||||
this.ctx = ctx;
|
|
||||||
const rect = res[0];
|
|
||||||
console.log('rect-----:', rect);
|
|
||||||
// const width = rect.width * dpr;
|
|
||||||
// const height = rect.height * dpr;
|
|
||||||
|
|
||||||
// 设置canvas的实际尺寸
|
|
||||||
// canvas.width = width;
|
|
||||||
// canvas.height = height;
|
|
||||||
// ctx.scale(dpr, dpr);
|
|
||||||
|
|
||||||
// 使用TextDrawServiceV3绘制
|
|
||||||
const textDrawService = new TextDrawServiceV3(canvas, ctx);
|
|
||||||
textDrawService.draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// const canvas = res[0].node;
|
|
||||||
// const ctx = canvas.getContext('2d');
|
|
||||||
// this.canvas = canvas;
|
|
||||||
// this.ctx = ctx;
|
|
||||||
|
|
||||||
// const textDrawService = new TextDrawServiceV3(canvas, ctx);
|
|
||||||
// textDrawService.draw();
|
|
||||||
|
|
||||||
// this.setCanvasSize(canvas, ctx);
|
|
||||||
// ctx.fillRect(0, 0, 2480, 3508);
|
|
||||||
// this.drawGameBoard(ctx);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setCanvasSize(canvas: Canvas, ctx: RenderingContext) {
|
|
||||||
const A4 = {
|
|
||||||
width: 2480,
|
|
||||||
height: 3508,
|
|
||||||
};
|
|
||||||
const { pixelRatio: dpr, windowWidth, windowHeight } = wx.getWindowInfo();
|
|
||||||
// const printWidth = A4.width;
|
|
||||||
// const printHeight = A4.height;
|
|
||||||
// @ts-ignore
|
|
||||||
const originWidth = canvas._width || windowWidth;
|
|
||||||
// @ts-ignore
|
|
||||||
const originHeight = canvas._height || windowHeight;
|
|
||||||
|
|
||||||
console.log('originWidth---:', originWidth);
|
|
||||||
console.log('originHeight---:', originHeight);
|
|
||||||
|
|
||||||
canvas.width = A4.width;
|
|
||||||
canvas.height = A4.height;
|
|
||||||
console.log('canvas----:', canvas);
|
|
||||||
|
|
||||||
// 计算显示比例
|
|
||||||
// const scaleRatio = Math.min(originWidth / printWidth, originHeight / printHeight);
|
|
||||||
// console.log('scaleRatio---:', scaleRatio);
|
|
||||||
// console.log('dpr---:', dpr);
|
|
||||||
// 设置坐标系
|
|
||||||
// ctx.scale(scaleRatio * dpr, scaleRatio * dpr);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 绘制完整游戏界面
|
|
||||||
drawGameBoard(ctx?: RenderingContext) {
|
|
||||||
const canvas = this.canvas as Canvas;
|
|
||||||
ctx = ctx || (this.ctx as RenderingContext);
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
ctx.fillStyle = '#fff'; // 设置填充色为白色
|
|
||||||
ctx.fillRect(0, 0, 2480, 3508); // 覆盖整个A4画布
|
|
||||||
|
|
||||||
// 绘制标题
|
|
||||||
// 绘制标题并居中显示
|
|
||||||
ctx.font = 'bold 80px "Microsoft Yahei"';
|
|
||||||
ctx.fillStyle = '#333';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
const titleX = canvas.width / 2;
|
|
||||||
const titleY = 150;
|
|
||||||
ctx.fillText('找一找 涂 色', titleX, titleY);
|
|
||||||
// 绘制颜色示例(顶部)
|
|
||||||
this.drawColorLegend(ctx, 250);
|
|
||||||
|
|
||||||
// 绘制汉字矩阵(主体)
|
|
||||||
this.drawCharacterMatrix(ctx, 600);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 绘制颜色示例
|
|
||||||
drawColorLegend(ctx: RenderingContext, y: number) {
|
|
||||||
console.log('this.data:', this.data);
|
|
||||||
let x = 500;
|
|
||||||
this.data.characters.forEach((char) => {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(x, y, 50, 0, Math.PI * 2);
|
|
||||||
ctx.fillStyle = this.data.colorMap[char] as string;
|
|
||||||
ctx.fill();
|
|
||||||
ctx.strokeStyle = '#333';
|
|
||||||
ctx.lineWidth = 4;
|
|
||||||
ctx.stroke();
|
|
||||||
x += 300;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 绘制汉字矩阵(6列x7行)
|
|
||||||
drawCharacterMatrix(ctx: RenderingContext, startY: number) {
|
|
||||||
const cols = 6;
|
|
||||||
const cellSize = 350; // 每个单元格大小
|
|
||||||
const colorMap = this.data.colorMap;
|
|
||||||
|
|
||||||
this.data.matrix.forEach((char: string, index) => {
|
|
||||||
char = char || '';
|
|
||||||
const row = Math.floor(index / cols);
|
|
||||||
const col = index % cols;
|
|
||||||
|
|
||||||
// 计算坐标
|
|
||||||
const x = 300 + col * cellSize;
|
|
||||||
const y = startY + row * cellSize;
|
|
||||||
|
|
||||||
// 绘制圆形
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(x, y, 120, 0, Math.PI * 2);
|
|
||||||
ctx.fillStyle = '#fff';
|
|
||||||
ctx.fill();
|
|
||||||
ctx.strokeStyle = '#333';
|
|
||||||
ctx.lineWidth = 4;
|
|
||||||
ctx.stroke();
|
|
||||||
|
|
||||||
// 绘制汉字
|
|
||||||
ctx.font = 'bold 100px "Microsoft Yahei"';
|
|
||||||
ctx.fillStyle = (colorMap[char] as string) || '';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
ctx.fillText(char, x, y);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 点击颜色选择
|
|
||||||
handleColorSelect(e: WechatMiniprogram.TouchEvent) {
|
|
||||||
const index = e.currentTarget.dataset.index;
|
|
||||||
const selectedChar = this.data.characters[index];
|
|
||||||
this.setData({
|
|
||||||
selectedColor: this.data.colorMap[selectedChar],
|
|
||||||
selectedChar,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 点击汉字涂色
|
|
||||||
handleCharacterTap(e: WechatMiniprogram.TouchEvent) {
|
|
||||||
const index = e.currentTarget.dataset.index;
|
|
||||||
if (!this.data.selectedColor) return;
|
|
||||||
|
|
||||||
const matrix = this.data.matrix.map((char, i) =>
|
|
||||||
i === index ? this.data.selectedChar : char,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.setData({ matrix }, () => {
|
|
||||||
this.drawGameBoard();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 导出A4图片
|
|
||||||
exportToPrint() {
|
|
||||||
console.log('this.canvas:', this.canvas);
|
|
||||||
if (this.canvas) {
|
|
||||||
wx.canvasToTempFilePath({
|
|
||||||
canvas: this.canvas,
|
|
||||||
fileType: 'png',
|
|
||||||
quality: 1,
|
|
||||||
success: (res) => {
|
|
||||||
// wx.getImageInfo({
|
|
||||||
// src: res.tempFilePath,
|
|
||||||
// success: (res) =>
|
|
||||||
// console.log('getImageInfo image size :', res.width, res.height), // 应输出2480x3508
|
|
||||||
// });
|
|
||||||
wx.saveImageToPhotosAlbum({
|
|
||||||
filePath: res.tempFilePath,
|
|
||||||
success: () => wx.showToast({ title: '保存成功' }),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<view class="container">
|
|
||||||
<!-- 颜色选择区 -->
|
|
||||||
<!-- <view class="color-picker">
|
|
||||||
<block wx:for="{{characters}}" wx:key="index">
|
|
||||||
<view
|
|
||||||
class="color-circle"
|
|
||||||
style="background: {{colorMap.get(item)}}"
|
|
||||||
bindtap="handleColorSelect"
|
|
||||||
data-index="{{index}}"></view>
|
|
||||||
</block>
|
|
||||||
</view> -->
|
|
||||||
<!-- 画布容器 -->
|
|
||||||
<canvas type="2d" id="textCanvas" class="canvas-container" style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
|
||||||
<view>当前宽度:{{boxWidth}}px,高度:{{boxHeight}}px</view>
|
|
||||||
<!-- 操作按钮 -->
|
|
||||||
<button class="action-btn" bindtap="exportToPrint">导出A4图片</button>
|
|
||||||
</view>
|
|
||||||
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 241 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 326 KiB |
|
After Width: | Height: | Size: 254 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 252 KiB |
|
After Width: | Height: | Size: 276 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "看数字,涂一涂",
|
||||||
|
"navigationBarBackgroundColor": "#FFD719",
|
||||||
|
"homeButton": true,
|
||||||
|
"backgroundColor": "#F6F6F6",
|
||||||
|
"enablePullDownRefresh": false,
|
||||||
|
"usingComponents": {
|
||||||
|
"toy-button": "../../ui/button/button",
|
||||||
|
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
page {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-bottom: 160rpx; // 为底部按钮预留空间
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
height: 50rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 36rpx 24rpx;
|
||||||
|
box-shadow: 0 6rpx 8rpx rgba(0, 0, 0, 0.15);
|
||||||
|
margin: 30rpx 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.wrapper-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #141414;
|
||||||
|
margin-bottom: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 400rpx;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border: 2rpx dashed #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-content {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .canvas-wrapper {
|
||||||
|
// width: 100%;
|
||||||
|
// margin-bottom: 40rpx;
|
||||||
|
// display: flex;
|
||||||
|
// justify-content: center;
|
||||||
|
// align-items: center;
|
||||||
|
// background: #f8f8f8;
|
||||||
|
// border-radius: 12rpx;
|
||||||
|
// padding: 20rpx;
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .canvas-content {
|
||||||
|
// width: 100%;
|
||||||
|
// height: 100%;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 数字选择区域 */
|
||||||
|
.number-selection-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 60rpx;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-item {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 2rpx solid #e8e8e8;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #93d333;
|
||||||
|
box-shadow: 0 0 0 4rpx rgba(147, 211, 51, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #141414;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 随机按钮样式 */
|
||||||
|
&.random-button {
|
||||||
|
flex: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
.random-icon {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.toy-icon {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #141414;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-text {
|
||||||
|
// flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #93d333;
|
||||||
|
box-shadow: 0 0 0 4rpx rgba(147, 211, 51, 0.2);
|
||||||
|
|
||||||
|
.random-icon .toy-icon {
|
||||||
|
color: #93d333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部按钮区域 */
|
||||||
|
.bottom-btn-box {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 200rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px -3.9rpx 5.2rpx rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 0 24rpx;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 16rpx 16rpx 0 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
import { PAPER_SIZE } from '../../constants/colors';
|
||||||
|
import { checkAndSaveImage } from '../../utils/saveImage';
|
||||||
|
import { shouldShowShareGuide } from '../../utils/shareGuide';
|
||||||
|
|
||||||
|
Page({
|
||||||
|
canvas: null as Canvas | null,
|
||||||
|
ctx: null as RenderingContext | null,
|
||||||
|
boxHeight: 0,
|
||||||
|
boxWidth: 0,
|
||||||
|
|
||||||
|
data: {
|
||||||
|
pageTitle: '看数字,涂一涂', // 默认标题
|
||||||
|
selectedNumber: 0, // 选中的数字
|
||||||
|
functionId: '', // 功能ID,用于判断标题
|
||||||
|
numberList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // 数字列表
|
||||||
|
showShareDialog: false, // 显示分享引导弹窗
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad(options: { id?: string }) {
|
||||||
|
// 根据id参数判断页面标题
|
||||||
|
const functionId = options.id || 'number-find-color';
|
||||||
|
this.setData({
|
||||||
|
functionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 根据functionId设置页面标题
|
||||||
|
const titleMap: Record<string, string> = {
|
||||||
|
'number-find-color': '看数字,涂一涂',
|
||||||
|
'number-coloring': '数字涂色卡',
|
||||||
|
'counting-matching': '数一数,连一连',
|
||||||
|
'missing-number': '填上缺少的数字',
|
||||||
|
compare: '数一数,比大小',
|
||||||
|
'counting-select': '数一数,选一选',
|
||||||
|
'counting-fill': '数一数,填一填',
|
||||||
|
'addition-5': '5以内加法',
|
||||||
|
'addition-10': '10以内加法',
|
||||||
|
'addition-subtraction-10': '10以内加减法',
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageTitle = titleMap[functionId] || '看数字,涂一涂';
|
||||||
|
this.setData({
|
||||||
|
pageTitle,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置导航栏标题
|
||||||
|
wx.setNavigationBarTitle({
|
||||||
|
title: pageTitle,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
// 初始化canvas
|
||||||
|
this.initCanvas();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化Canvas
|
||||||
|
*/
|
||||||
|
initCanvas() {
|
||||||
|
const query = wx.createSelectorQuery();
|
||||||
|
query
|
||||||
|
.select('#canvasWrapper')
|
||||||
|
.boundingClientRect((rect) => {
|
||||||
|
if (rect) {
|
||||||
|
const { width, height } = PAPER_SIZE['A4'];
|
||||||
|
const boxWidth = rect.width;
|
||||||
|
const boxHeight = boxWidth / (width / height);
|
||||||
|
|
||||||
|
this.boxHeight = boxHeight;
|
||||||
|
this.boxWidth = boxWidth;
|
||||||
|
|
||||||
|
this.setData({
|
||||||
|
boxWidth,
|
||||||
|
boxHeight,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化canvas上下文
|
||||||
|
const canvas = wx
|
||||||
|
.createSelectorQuery()
|
||||||
|
.select('#canvasContent');
|
||||||
|
canvas.fields({ node: true, size: true }).exec((res) => {
|
||||||
|
if (res[0]) {
|
||||||
|
const canvasNode = res[0].node;
|
||||||
|
const ctx = canvasNode.getContext('2d');
|
||||||
|
const dpr = wx.getSystemInfoSync().pixelRatio;
|
||||||
|
|
||||||
|
canvasNode.width = boxWidth * dpr;
|
||||||
|
canvasNode.height = boxHeight * dpr;
|
||||||
|
ctx.scale(dpr, dpr);
|
||||||
|
|
||||||
|
this.canvas = canvasNode;
|
||||||
|
this.ctx = ctx;
|
||||||
|
|
||||||
|
// 绘制初始内容
|
||||||
|
this.drawCanvas();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制Canvas内容
|
||||||
|
*/
|
||||||
|
drawCanvas() {
|
||||||
|
if (!this.ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = this.ctx;
|
||||||
|
const width = this.boxWidth;
|
||||||
|
const height = this.boxHeight;
|
||||||
|
|
||||||
|
// 清空画布
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// 设置背景色
|
||||||
|
ctx.fillStyle = '#FFFFFF';
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// TODO: 根据选中的数字绘制内容
|
||||||
|
if (this.data.selectedNumber > 0) {
|
||||||
|
// 绘制示例:显示选中的数字
|
||||||
|
ctx.fillStyle = '#141414';
|
||||||
|
ctx.font = 'bold 120px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText(
|
||||||
|
String(this.data.selectedNumber),
|
||||||
|
width / 2,
|
||||||
|
height / 2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择数字
|
||||||
|
*/
|
||||||
|
onSelectNumber(e: WechatMiniprogram.TouchEvent) {
|
||||||
|
const number = parseInt(e.currentTarget.dataset.number);
|
||||||
|
this.setData({
|
||||||
|
selectedNumber: number,
|
||||||
|
});
|
||||||
|
this.drawCanvas();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机选择数字
|
||||||
|
*/
|
||||||
|
onRandom() {
|
||||||
|
const randomNumber = Math.floor(Math.random() * 10) + 1;
|
||||||
|
this.setData({
|
||||||
|
selectedNumber: randomNumber,
|
||||||
|
});
|
||||||
|
this.drawCanvas();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出打印
|
||||||
|
*/
|
||||||
|
exportToPrint() {
|
||||||
|
if (!this.canvas || this.data.selectedNumber <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否需要显示分享引导
|
||||||
|
if (shouldShowShareGuide()) {
|
||||||
|
this.setData({ showShareDialog: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接下载
|
||||||
|
checkAndSaveImage(this.canvas);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享小程序
|
||||||
|
*/
|
||||||
|
onShareAppMessage() {
|
||||||
|
return {
|
||||||
|
title: '涂鸦丫-数学学习涂鸦卡',
|
||||||
|
path: `/mathPages/numberColor/numberColor?id=${this.data.functionId}`,
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onShareTimeline() {
|
||||||
|
return {
|
||||||
|
title: '涂鸦丫-数学学习涂鸦卡',
|
||||||
|
query: `id=${this.data.functionId}`,
|
||||||
|
imageUrl:
|
||||||
|
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 关闭分享引导弹窗 */
|
||||||
|
onCloseShareDialog() {
|
||||||
|
this.setData({ showShareDialog: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 分享成功回调 */
|
||||||
|
onShareSuccess() {
|
||||||
|
this.setData({ showShareDialog: false });
|
||||||
|
// 分享成功后直接下载
|
||||||
|
if (this.canvas) {
|
||||||
|
checkAndSaveImage(this.canvas);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
<view class="page-container">
|
||||||
|
<view class="wrapper">
|
||||||
|
<text class="wrapper-title">预览打印效果</text>
|
||||||
|
|
||||||
|
<!-- 预览打印效果 -->
|
||||||
|
<view id="canvasWrapper" class="canvas-wrapper">
|
||||||
|
<canvas
|
||||||
|
type="2d"
|
||||||
|
id="canvasContent"
|
||||||
|
class="canvas-content"
|
||||||
|
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 数字选择区域 -->
|
||||||
|
<view class="number-selection-area">
|
||||||
|
<!-- 第一行:1-4 -->
|
||||||
|
<view class="number-row">
|
||||||
|
<view
|
||||||
|
wx:for="{{numberList}}"
|
||||||
|
wx:key="index"
|
||||||
|
wx:for-item="number"
|
||||||
|
wx:if="{{index < 4}}"
|
||||||
|
class="number-item {{selectedNumber === number ? 'active' : ''}}"
|
||||||
|
data-number="{{number}}"
|
||||||
|
bind:tap="onSelectNumber">
|
||||||
|
<view class="number-text">{{number}}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 第二行:5-8 -->
|
||||||
|
<view class="number-row">
|
||||||
|
<view
|
||||||
|
wx:for="{{numberList}}"
|
||||||
|
wx:key="index"
|
||||||
|
wx:for-item="number"
|
||||||
|
wx:if="{{index >= 4 && index < 8}}"
|
||||||
|
class="number-item {{selectedNumber === number ? 'active' : ''}}"
|
||||||
|
data-number="{{number}}"
|
||||||
|
bind:tap="onSelectNumber">
|
||||||
|
<view class="number-text">{{number}}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 第三行:9、10和随机按钮 -->
|
||||||
|
<view class="number-row">
|
||||||
|
<view
|
||||||
|
wx:for="{{numberList}}"
|
||||||
|
wx:key="index"
|
||||||
|
wx:for-item="number"
|
||||||
|
wx:if="{{index >= 8}}"
|
||||||
|
class="number-item {{selectedNumber === number ? 'active' : ''}}"
|
||||||
|
data-number="{{number}}"
|
||||||
|
bind:tap="onSelectNumber">
|
||||||
|
<view class="number-text">{{number}}</view>
|
||||||
|
</view>
|
||||||
|
<view class="number-item random-button" bind:tap="onRandom">
|
||||||
|
<view class="random-icon">
|
||||||
|
<text class="toy-icon toy-icon-refresh"></text>
|
||||||
|
</view>
|
||||||
|
<view class="number-text">随机</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="empty"></view>
|
||||||
|
</view>
|
||||||
|
<view class="bottom-btn-box">
|
||||||
|
<toy-button
|
||||||
|
openType="share"
|
||||||
|
type="green"
|
||||||
|
flat="{{true}}"
|
||||||
|
bind:click="onShareAppMessage"
|
||||||
|
width="220rpx"
|
||||||
|
height="80rpx"
|
||||||
|
icon="wechat"
|
||||||
|
icon-class-prefix="toy-icon">
|
||||||
|
分享
|
||||||
|
</toy-button>
|
||||||
|
<toy-button
|
||||||
|
type="primary"
|
||||||
|
flat="{{true}}"
|
||||||
|
bind:click="exportToPrint"
|
||||||
|
width="420rpx"
|
||||||
|
height="80rpx"
|
||||||
|
disabled="{{selectedNumber <= 0}}"
|
||||||
|
icon="download"
|
||||||
|
icon-class-prefix="toy-icon">
|
||||||
|
下载打印
|
||||||
|
</toy-button>
|
||||||
|
</view>
|
||||||
|
<share-guide-popup
|
||||||
|
show="{{showShareDialog}}"
|
||||||
|
bind:onClose="onCloseShareDialog"
|
||||||
|
bind:onShareSuccess="onShareSuccess" />
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"navigationBarTitleText": "按钮组件测试",
|
|
||||||
"navigationBarBackgroundColor": "#FFD719",
|
|
||||||
"backgroundColor": "#F6F6F6",
|
|
||||||
"usingComponents": {
|
|
||||||
"toy-button": "../../ui/button/button",
|
|
||||||
"van-icon": "@vant/weapp/icon/index",
|
|
||||||
"word-card": "../../components/word-card/word-card"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
page {
|
|
||||||
background-color: #F6F6F6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-test-container {
|
|
||||||
padding: 40rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-section {
|
|
||||||
margin-bottom: 60rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
display: block;
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #141414;
|
|
||||||
margin-bottom: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 24rpx;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.word-cards-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 32rpx;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600rpx;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
Page({
|
|
||||||
data: {},
|
|
||||||
|
|
||||||
onButtonClick(e: WechatMiniprogram.TouchEvent) {
|
|
||||||
console.log('按钮点击:', e);
|
|
||||||
wx.showToast({
|
|
||||||
title: '按钮被点击了',
|
|
||||||
icon: 'success',
|
|
||||||
duration: 1000
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onWordColorTap(e: any) {
|
|
||||||
console.log('点击了颜色:', e);
|
|
||||||
wx.showToast({
|
|
||||||
title: '点击了颜色',
|
|
||||||
icon: 'none',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onWordDelete(e: any) {
|
|
||||||
console.log('删除文字:', e);
|
|
||||||
wx.showToast({
|
|
||||||
title: '删除文字',
|
|
||||||
icon: 'none',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
<view class="button-test-container">
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">主要按钮 (Primary)</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button type="primary" bind:click="onButtonClick"
|
|
||||||
>主要按钮</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="primary" disabled bind:click="onButtonClick"
|
|
||||||
>禁用状态</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="primary" loading bind:click="onButtonClick"
|
|
||||||
>加载中</toy-button
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">次要按钮 (Secondary)</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button type="secondary" bind:click="onButtonClick"
|
|
||||||
>次要按钮</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="secondary" disabled bind:click="onButtonClick"
|
|
||||||
>禁用状态</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="secondary" loading bind:click="onButtonClick"
|
|
||||||
>加载中</toy-button
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">绿色按钮 (Green)</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button type="green" bind:click="onButtonClick"
|
|
||||||
>绿色按钮</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="green" disabled bind:click="onButtonClick"
|
|
||||||
>禁用状态</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="green" loading bind:click="onButtonClick"
|
|
||||||
>加载中</toy-button
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">白色按钮 (White)</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button type="white" bind:click="onButtonClick"
|
|
||||||
>白色按钮</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="white" disabled bind:click="onButtonClick"
|
|
||||||
>禁用状态</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="white" plain bind:click="onButtonClick"
|
|
||||||
>透明模式</toy-button
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">扁平化按钮 (Flat)</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button type="primary" flat bind:click="onButtonClick"
|
|
||||||
>扁平主要</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="secondary" flat bind:click="onButtonClick"
|
|
||||||
>扁平次要</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="white" flat bind:click="onButtonClick"
|
|
||||||
>扁平白色</toy-button
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">带图标按钮</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button type="primary" icon="add" bind:click="onButtonClick"
|
|
||||||
>添加</toy-button
|
|
||||||
>
|
|
||||||
<toy-button
|
|
||||||
type="secondary"
|
|
||||||
icon="delete"
|
|
||||||
bind:click="onButtonClick"
|
|
||||||
>删除</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="white" icon="edit" bind:click="onButtonClick"
|
|
||||||
>编辑</toy-button
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">不同尺寸</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button
|
|
||||||
type="primary"
|
|
||||||
width="200rpx"
|
|
||||||
bind:click="onButtonClick">
|
|
||||||
小按钮
|
|
||||||
</toy-button>
|
|
||||||
<toy-button
|
|
||||||
type="secondary"
|
|
||||||
width="300rpx"
|
|
||||||
bind:click="onButtonClick"
|
|
||||||
>中按钮</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="white" width="400rpx" bind:click="onButtonClick">
|
|
||||||
大按钮
|
|
||||||
</toy-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">点击动效测试</text>
|
|
||||||
<view class="button-group">
|
|
||||||
<toy-button type="primary" bind:click="onButtonClick"
|
|
||||||
>点击我测试动效</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="secondary" bind:click="onButtonClick"
|
|
||||||
>抖动效果</toy-button
|
|
||||||
>
|
|
||||||
<toy-button type="white" bind:click="onButtonClick"
|
|
||||||
>缩放动画</toy-button
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="test-section">
|
|
||||||
<text class="section-title">Word Card 测试</text>
|
|
||||||
<view class="word-cards-grid">
|
|
||||||
<word-card
|
|
||||||
word="王"
|
|
||||||
color="#FFD719"
|
|
||||||
bind:colorTap="onWordColorTap"
|
|
||||||
bind:delete="onWordDelete">
|
|
||||||
</word-card>
|
|
||||||
<word-card
|
|
||||||
word="手"
|
|
||||||
color="#93D333"
|
|
||||||
bind:colorTap="onWordColorTap"
|
|
||||||
bind:delete="onWordDelete">
|
|
||||||
</word-card>
|
|
||||||
<word-card
|
|
||||||
word="哭"
|
|
||||||
color="#FF6B9D"
|
|
||||||
bind:colorTap="onWordColorTap"
|
|
||||||
bind:delete="onWordDelete">
|
|
||||||
</word-card>
|
|
||||||
<word-card
|
|
||||||
word="少"
|
|
||||||
color="#4ECDC4"
|
|
||||||
bind:colorTap="onWordColorTap"
|
|
||||||
bind:delete="onWordDelete">
|
|
||||||
</word-card>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "涂鸦丫-数学",
|
||||||
|
"navigationBarBackgroundColor": "#FFD719",
|
||||||
|
"homeButton": true,
|
||||||
|
"backgroundColor": "#F6F6F6",
|
||||||
|
"enablePullDownRefresh": false,
|
||||||
|
"usingComponents": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
page {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
height: 190rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 数学页面标题和描述 */
|
||||||
|
.math-page-header {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-page-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #141414;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-page-subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 数学功能列表样式 */
|
||||||
|
.math-functions-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-function-item {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 2rpx solid #e8e8e8;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24rpx;
|
||||||
|
// box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
opacity: 0;
|
||||||
|
background: rgba(147, 211, 51, 0.1);
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
&::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-color: #93d333;
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(147, 211, 51, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-item-image {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-image-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, rgba(255, 215, 25, 0.2) 0%, rgba(255, 169, 64, 0.2) 100%);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 56rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-item-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-item-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #141414;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-item-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-item-arrow {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #999999;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 使用提示 */
|
||||||
|
.usage-hint {
|
||||||
|
background: linear-gradient(135deg, #fff7e6 0%, #fffbe6 100%);
|
||||||
|
border: 2rpx solid #ffd719;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 24rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-icon {
|
||||||
|
font-size: 40rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #141414;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
functionList: [
|
||||||
|
{
|
||||||
|
id: 'number-find-color',
|
||||||
|
page: 'numberColor',
|
||||||
|
title: '看数字,涂一涂',
|
||||||
|
desc: '给一个数字,找一找下面相同的数字,涂上颜色',
|
||||||
|
icon: '🔍',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'number-coloring',
|
||||||
|
title: '数字涂色卡',
|
||||||
|
desc: '一边写数字、一边学数数,数字和卡通图片一一对应',
|
||||||
|
icon: '🎨',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'counting-matching',
|
||||||
|
title: '数一数,连一连',
|
||||||
|
desc: '通过连线配对数字和对应的数量图形',
|
||||||
|
icon: '🔗',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'missing-number',
|
||||||
|
title: '填上缺少的数字',
|
||||||
|
desc: '在数字序列中找出并填写缺失的数字',
|
||||||
|
icon: '❓',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'compare',
|
||||||
|
title: '数一数,比大小',
|
||||||
|
desc: '通过数数比较两组物品的数量大小',
|
||||||
|
icon: '⚖️',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'counting-select',
|
||||||
|
title: '数一数,选一选',
|
||||||
|
desc: '数出物品数量,从多个选项中选择正确答案',
|
||||||
|
icon: '✓',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'counting-fill',
|
||||||
|
title: '数一数,填一填',
|
||||||
|
desc: '数出物品数量,填写对应的数字',
|
||||||
|
icon: '✏️',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'addition-5',
|
||||||
|
title: '5以内加法',
|
||||||
|
desc: '练习5以内的加法运算,图形化展示',
|
||||||
|
icon: '➕',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'addition-10',
|
||||||
|
title: '10以内加法',
|
||||||
|
desc: '练习10以内的加法运算,图形化展示',
|
||||||
|
icon: '➕',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'addition-subtraction-10',
|
||||||
|
title: '10以内加减法',
|
||||||
|
desc: '练习10以内的加法和减法混合运算',
|
||||||
|
icon: '±',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
// 页面加载
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开数学功能页面
|
||||||
|
*/
|
||||||
|
openMathFunction(e: WechatMiniprogram.TouchEvent) {
|
||||||
|
const functionId = e.currentTarget.dataset.function;
|
||||||
|
const functionItem = this.data.functionList.find(
|
||||||
|
(item) => item.id === functionId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!functionItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有page字段,跳转到对应页面
|
||||||
|
if (functionItem.page) {
|
||||||
|
wx.navigateTo({
|
||||||
|
url: `/mathPages/${functionItem.page}/${functionItem.page}?id=${functionId}`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
wx.showToast({
|
||||||
|
title: `即将打开:${functionItem.title}`,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<view class="page-container">
|
||||||
|
<!-- 页面标题和描述 -->
|
||||||
|
<view class="math-page-header">
|
||||||
|
<view class="math-page-title">数学学习涂鸦卡</view>
|
||||||
|
<view class="math-page-subtitle"
|
||||||
|
>选择功能,生成可打印的数学学习涂鸦卡</view
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 功能列表 -->
|
||||||
|
<view class="math-functions-list">
|
||||||
|
<view
|
||||||
|
class="math-function-item"
|
||||||
|
wx:for="{{functionList}}"
|
||||||
|
wx:key="id"
|
||||||
|
data-function="{{item.id}}"
|
||||||
|
bind:tap="openMathFunction">
|
||||||
|
<view class="function-item-image">
|
||||||
|
<view class="function-image-placeholder">{{item.icon}}</view>
|
||||||
|
</view>
|
||||||
|
<view class="function-item-content">
|
||||||
|
<view class="function-item-title">{{item.title}}</view>
|
||||||
|
<view class="function-item-desc">{{item.desc}}</view>
|
||||||
|
</view>
|
||||||
|
<view class="function-item-arrow">→</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 使用提示 -->
|
||||||
|
<view class="usage-hint">
|
||||||
|
<view class="hint-icon">💡</view>
|
||||||
|
<view class="hint-text">
|
||||||
|
<view class="hint-title">使用提示</view>
|
||||||
|
<view class="hint-desc">
|
||||||
|
1. 点击上方功能卡片选择要生成的涂鸦卡类型\n 2.
|
||||||
|
选择数字范围、图形类型等参数\n 3. 预览生成的涂鸦卡效果\n 4.
|
||||||
|
下载A4格式PNG图片,打印后使用
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="empty"></view>
|
||||||
|
</view>
|
||||||
@@ -23,12 +23,26 @@
|
|||||||
"condition": {
|
"condition": {
|
||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"name": "mathPages/numberColor/numberColor",
|
||||||
|
"pathName": "mathPages/numberColor/numberColor",
|
||||||
|
"query": "id=number-find-color",
|
||||||
|
"scene": null,
|
||||||
|
"launchMode": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pages/mathIndex/mathIndex",
|
||||||
|
"pathName": "pages/mathIndex/mathIndex",
|
||||||
|
"query": "",
|
||||||
|
"launchMode": "default",
|
||||||
|
"scene": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pages/copyBook/copyBook",
|
"name": "pages/copyBook/copyBook",
|
||||||
"pathName": "pages/copyBook/copyBook",
|
"pathName": "pages/copyBook/copyBook",
|
||||||
"query": "",
|
"query": "",
|
||||||
"scene": null,
|
"launchMode": "default",
|
||||||
"launchMode": "default"
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pages/buttonTest/buttonTest",
|
"name": "pages/buttonTest/buttonTest",
|
||||||
|
|||||||