diff --git a/miniprogram/app.json b/miniprogram/app.json index fb5d5bb..960d354 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -2,10 +2,15 @@ "pages": [ "pages/index/index", "pages/shape/index", - "pages/debug/debug", - "pages/wordDemo/index", + "pages/mathIndex/mathIndex", "pages/copyBook/copyBook", - "pages/buttonTest/buttonTest" + "pages/debug/debug" + ], + "subPackages": [ + { + "root": "mathPages", + "pages": ["numberColor/numberColor"] + } ], "window": {}, "style": "v2", @@ -37,6 +42,12 @@ "selectedIconPath": "assets/tabBar/icon-word-active.png", "text": "识字" }, + { + "pagePath": "pages/mathIndex/mathIndex", + "iconPath": "assets/tabBar/icon-math.png", + "selectedIconPath": "assets/tabBar/icon-math-active.png", + "text": "数学" + }, { "pagePath": "pages/copyBook/copyBook", "iconPath": "assets/tabBar/icon-edit.png", diff --git a/miniprogram/assets/tabBar/icon-math-active.png b/miniprogram/assets/tabBar/icon-math-active.png new file mode 100644 index 0000000..feea51d Binary files /dev/null and b/miniprogram/assets/tabBar/icon-math-active.png differ diff --git a/miniprogram/assets/tabBar/icon-math.png b/miniprogram/assets/tabBar/icon-math.png new file mode 100644 index 0000000..6c25d9b Binary files /dev/null and b/miniprogram/assets/tabBar/icon-math.png differ diff --git a/miniprogram/canvasService/constant.ts b/miniprogram/canvasService/constant.ts deleted file mode 100644 index 4858c35..0000000 --- a/miniprogram/canvasService/constant.ts +++ /dev/null @@ -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 }; diff --git a/miniprogram/canvasService/textDrawService.ts b/miniprogram/canvasService/textDrawService.ts deleted file mode 100644 index 1a69df2..0000000 --- a/miniprogram/canvasService/textDrawService.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { PAPER_SIZE } from './constant'; -import { getMiniCodeImagePath } from './utils'; - -class TextDrawService { - canvas: WechatMiniprogram.Canvas; - ctx: RenderingContext; - options: Record; - paperSize: PaperSize; - currentX: number; - currentY: number; - - constructor(canvas: Canvas, ctx: RenderingContext, options?: Record) { - this.canvas = canvas; - this.ctx = ctx; - this.paperSize = 'A4'; - this.options = { - appName: '涂鸦呀', - appHint: '涂色|识字|画画|认知', - title: '找一找 涂 色', - subTitle: '把相同的文字涂上相同的颜色', - radius: 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; diff --git a/miniprogram/canvasService/textDrawServiceV2.ts b/miniprogram/canvasService/textDrawServiceV2.ts deleted file mode 100644 index f692c97..0000000 --- a/miniprogram/canvasService/textDrawServiceV2.ts +++ /dev/null @@ -1,237 +0,0 @@ -// import { PAPER_SIZE } from './constant'; -import { getMiniCodeImage } from './utils'; - -class TextDrawService { - canvas: WechatMiniprogram.Canvas; - ctx: RenderingContext; - options: Record; - paperSize: PaperSize; - currentX: number; - currentY: number; - - constructor(canvas: Canvas, ctx: RenderingContext, options?: Record) { - this.canvas = canvas; - this.ctx = ctx; - this.paperSize = 'A4'; - this.options = { - appName: '涂鸦呀', - appHint: '涂色|识字|画画|认知', - title: '找一找 涂 色', - subTitle: '把相同的文字涂上相同的颜色', - radius: 36, - colors: ['#2196F3', '#FFEB3B', '#FF9800', '#F44336', '#F44336'], - characters: ['鱼', '鸟', '猫', '狗', '牛'], - ...options, - }; - this.currentX = 0; - this.currentY = 0; - } - - draw() { - this.setPaper(); - // this.setPaper3(); - this.drawHeader(); - // this.drawLegend(); - } - - async drawHeader() { - const { canvas, ctx } = this; - const { appName, appHint, title, subTitle } = this.options; - - this.currentX = 60; - this.currentY = 60; - const titleX = this.currentX + 140 + 14; - const titleY = 87; - const image = await getMiniCodeImage(canvas); - ctx.drawImage(image, 60, 60, 140, 140); - - ctx.font = 'bold 36px "Microsoft Yahei"'; - ctx.fillStyle = '#000'; - ctx.textAlign = 'left'; - ctx.textBaseline = 'top'; - - ctx.fillText(appName, titleX, titleY); - - ctx.font = '24px "Microsoft Yahei"'; - ctx.fillStyle = '#666'; - ctx.fillText(appHint, titleX, 163); - - // 绘制分割线 - ctx.strokeStyle = '#888'; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(483, 70); - ctx.lineTo(483, 70 + 140); - ctx.stroke(); - - ctx.font = 'bold 36px "Microsoft Yahei"'; - ctx.fillStyle = '#000'; - ctx.fillText(title, 538, titleY); - - ctx.font = '24px "Microsoft Yahei"'; - ctx.fillStyle = '#666'; - ctx.fillText(subTitle, 538, 163); - - this.drawLegend(); - } - - /** 绘制示例 - * 矩形: x、y为左上角 - * 圆形:x、y为圆心 - * 文字:x textAlign 为 start,文本左边缘对齐 - * y textBaseline 为 alphabetic,y 对应字母基线(类似左下角) - * - * */ - drawLegend() { - const { canvas, ctx, options } = this; - const { colors, characters, radius } = options; - const rectWidth = 100; - const rectHeight = 48; - const startX = 978; - const startY = 60 + radius; // 设置起始Y坐标 - const len = characters.length || 4; - const canvasWidth = canvas.width; - /** 计算每个圆之间的间距 俩个60,一个是页面右侧边距,另一个是圆离右边距地边距*/ - const spaceWidth = (canvasWidth - startX - 60 - 60 - radius * (len - 1)) / (len - 1); - - ctx.moveTo(startX, startY); - colors.forEach((color: string, index: number) => { - const x = startX + index * (spaceWidth + radius); - const y = startY; // 计算圆的Y坐标 - - // 绘制圆 - ctx.strokeStyle = '#333'; - ctx.fillStyle = color; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.arc(x, y, radius, 0, Math.PI * 2); - ctx.fill(); - ctx.stroke(); - ctx.closePath(); - - // 绘制长方形 - ctx.fillStyle = '#fff'; // 长方形背景色 - ctx.strokeStyle = '#333'; - ctx.lineWidth = 2; - const rectangleX = x - rectWidth / 2; // 从圆形移动一半的长方形的长 - const rectangleY = y + radius + 18; // 从圆形下移一个半径,再加上18的间距 - ctx.strokeRect(rectangleX, rectangleY, rectWidth, rectHeight); // 绘制长方形 - - // 绘制字符 - ctx.fillStyle = '#333'; - ctx.font = 'bold 24px "Microsoft Yahei"'; - ctx.textBaseline = 'top'; - ctx.fillText(characters[index], rectangleX + 38, 163); - }); - - this.currentY = 220; // 计算分割线的Y坐标,距离示例图20px - this.drawLine(this.currentY); - } - - drawLine(linY: number) { - const { canvas, ctx } = this; - - ctx.strokeStyle = '#333'; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(20, linY); - ctx.lineTo(canvas.width - 20, linY); - ctx.stroke(); - } - - setPaper() { - const { ctx, canvas } = this; - const { pixelRatio: dpr } = wx.getWindowInfo(); - const { width, height } = { width: 595 * dpr, height: 842 * dpr }; - - canvas.width = width; - canvas.height = height; - - this.clear(); - ctx.fillStyle = '#fff'; // 设置背景色为白色 - ctx.fillRect(0, 0, width, height); - } - - setPaper3() { - const { ctx, canvas } = this; - const { pixelRatio: dpr } = wx.getWindowInfo(); - // @ts-ignore - const winWidth = canvas._width; - // @ts-ignore - const winHeight = canvas._height; // 容器逻辑尺寸 - - // 计算适配后的物理尺寸 - const targetWidth = winWidth * dpr; // 宽度100%适配容器 - const aspectRatio = 842 / 595; // 原图宽高比(示例为A4纸比例) - let targetHeight = targetWidth * aspectRatio; // 按比例计算高度 - - // 判断是否需要裁剪高度 - if (targetHeight > winHeight * dpr) { - targetHeight = winHeight * dpr; // 若超出容器高度则强制裁剪 - } - - // 设置 Canvas 物理像素 - canvas.width = targetWidth; - canvas.height = targetHeight; - - console.log('targetWidth---:', targetWidth); - console.log('targetHeight---:', targetHeight); - - this.clear(); - ctx.fillStyle = '#fff'; // 设置背景色为白色 - ctx.fillRect(0, 0, targetWidth, targetHeight); - } - - setPaper2() { - const { canvas, ctx } = this; - const { pixelRatio: dpr } = wx.getWindowInfo(); - const { width, height } = { width: 595 * dpr, height: 842 * dpr }; - // @ts-ignore - const winWidth = canvas._width; - // @ts-ignore - const winHeight = canvas._height; - - const canvasAspect = width / height; // Canvas 内容的宽高比 - const containerAspect = winHeight / winWidth; // 容器的宽高比 - - // 选择缩放比例较小的方向(确保内容完全显示) - let scaleRatio = - containerAspect > canvasAspect - ? winWidth / width // 以宽度为基准 - : winHeight / height; // 以高度为基准 - - console.log('scaleRatio-----:', scaleRatio); - - const scaledWidth = width * scaleRatio; - const scaledHeight = height * scaleRatio; - - // 避免超过小程序画布最大限制(4096) - if (scaledWidth > 4096 || scaledHeight > 4096) { - scaleRatio *= Math.min(4096 / scaledWidth, 4096 / scaledHeight); - } - - console.log('scaledWidth-----:', scaledWidth); - console.log('scaledHeight-----:', scaledHeight); - - canvas.width = scaledWidth; - canvas.height = scaledHeight; - - ctx.translate((winWidth * dpr) / 2, (winHeight * dpr) / 2); - ctx.scale(scaleRatio * dpr, scaleRatio * dpr); - ctx.translate(-width / 2, -height / 2); // 将内容原点移至中心 - - // this.canvas.width = width; - // this.canvas.height = height; - - this.clear(); - ctx.fillStyle = '#fff'; // 设置背景色为白色 - ctx.fillRect(0, 0, width, height); - } - - clear() { - const canvas = this.canvas; - this.ctx.clearRect(0, 0, canvas.width, canvas.height); - } -} - -export default TextDrawService; diff --git a/miniprogram/canvasService/textDrawServiceV3.ts b/miniprogram/canvasService/textDrawServiceV3.ts deleted file mode 100644 index 99104c9..0000000 --- a/miniprogram/canvasService/textDrawServiceV3.ts +++ /dev/null @@ -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; - paperSize: PaperSize; - currentX: number; - currentY: number; - - constructor( - canvas: Canvas, - ctx: RenderingContext, - options?: Record, - ) { - this.canvas = canvas; - this.ctx = ctx; - this.paperSize = 'A4'; - this.options = { - appName: '涂鸦呀', - appHint: '涂色|识字|画画|认知', - title: '找一找 涂 色', - subTitle: '把相同的文字涂上相同的颜色', - 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; diff --git a/miniprogram/canvasService/utils.ts b/miniprogram/canvasService/utils.ts deleted file mode 100644 index a4ddaaf..0000000 --- a/miniprogram/canvasService/utils.ts +++ /dev/null @@ -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; - }); -} diff --git a/miniprogram/demoPages/shapePrint/index.js b/miniprogram/demoPages/shapePrint/index.js deleted file mode 100644 index b4b2ef3..0000000 --- a/miniprogram/demoPages/shapePrint/index.js +++ /dev/null @@ -1,1103 +0,0 @@ -// 引入形状和颜色数据 -import { shapes, colors } from './shapes'; -import { WATER_COLORS } from '../../constants/colors'; - -Page({ - data: { - // 可选择的形状列表 - availableShapes: shapes, - // 已选择的形状列表 - selectedShapes: [], - // 基础12色 - basic12Colors: WATER_COLORS.basic12, - // 已使用的颜色索引(确保不重复) - usedColorIndexes: [], - // 生成的图片 - generatedImage: null, - // 是否正在生成 - isGenerating: false, - // 背景选项 - backgrounds: [ - { name: '纯白', value: '#FFFFFF' }, - { name: '米黄', value: '#FAF5E8' }, - { name: '淡灰', value: '#F5F5F5' }, - ], - bgColor: '#FFFFFF', - // 弹窗控制 - showShapeSelector: false, - showColorPicker: false, - // 当前编辑的形状索引 - currentEditingIndex: -1, - currentEditingColor: '', - // 临时选择的形状ID列表(弹窗中的选择状态) - tempSelectedShapeIds: [], - }, - - onLoad() { - console.log('形状绘制页面加载完成'); - - // 为可选形状生成SVG内容 - const shapesWithSVG = this.data.availableShapes.map((shape) => ({ - ...shape, - svgContent: this.createSVGElement(shape, 'transparent', '#333', 50), - })); - - // 初始化临时选择列表 - this.setData({ - availableShapes: shapesWithSVG, - tempSelectedShapeIds: this.data.selectedShapes.map( - (shape) => shape.id, - ), - }); - }, - - // 生成带颜色的SVG字符串 - generateColoredSVG(shape, fillColor = '#666', borderColor = '#333') { - if (!shape || !shape.svg) return ''; - - let svg = shape.svg; - - // 替换stroke属性 - svg = svg.replace(/stroke="[^"]*"/g, `stroke="${borderColor}"`); - - // 处理fill属性 - if (fillColor && fillColor !== 'transparent') { - svg = svg.replace(/fill="[^"]*"/g, `fill="${fillColor}"`); - } - - // 确保stroke-width存在 - if (!svg.includes('stroke-width')) { - svg = svg.replace( - /(<(?:circle|rect|polygon|ellipse|path|line)[^>]*?)>/g, - '$1 stroke-width="2">', - ); - } - - return svg; - }, - - // 创建SVG的base64数据URL - createSVGElement(shape, fillColor, borderColor, size = 60) { - const coloredSVG = this.generateColoredSVG( - shape, - fillColor, - borderColor, - ); - - const svgContent = `${coloredSVG}`; - - // 转换为base64 - const base64 = wx.arrayBufferToBase64( - new TextEncoder().encode(svgContent), - ); - - // 可选:调试日志(发布时移除) - // console.log(`Creating SVG for ${shape.name}:`, { svgContent }); - - return `data:image/svg+xml;base64,${base64}`; - }, - - // 打开形状选择器 - openShapeSelector() { - this.setData({ - showShapeSelector: true, - // 将当前已选择的形状ID设置到临时列表中 - tempSelectedShapeIds: this.data.selectedShapes.map( - (shape) => shape.shapeId, - ), - }); - }, - - // 关闭形状选择器 - closeShapeSelector() { - this.setData({ - showShapeSelector: false, - }); - }, - - // 切换形状选择状态 - toggleShape(e) { - const shapeId = e.currentTarget.dataset.shapeId; - const { tempSelectedShapeIds } = this.data; - - if (tempSelectedShapeIds.includes(shapeId)) { - // 取消选择 - const newTempSelected = tempSelectedShapeIds.filter( - (id) => id !== shapeId, - ); - this.setData({ - tempSelectedShapeIds: newTempSelected, - }); - } else { - // 添加选择(最多6个) - if (tempSelectedShapeIds.length < 6) { - this.setData({ - tempSelectedShapeIds: [...tempSelectedShapeIds, shapeId], - }); - } else { - wx.showToast({ - title: '最多只能选择6个形状', - icon: 'none', - duration: 2000, - }); - } - } - }, - - // 确认形状选择 - confirmShapeSelection() { - const { tempSelectedShapeIds, availableShapes, selectedShapes } = - this.data; - - // 构建新的选择列表 - const newSelectedShapes = []; - const usedColorIndexes = []; - - tempSelectedShapeIds.forEach((shapeId, index) => { - const shape = availableShapes.find((s) => s.id === shapeId); - if (shape) { - // 查找是否已存在该形状 - const existingShape = selectedShapes.find( - (s) => s.shapeId === shapeId, - ); - if (existingShape) { - // 保持原有颜色,更新SVG内容 - const updatedShape = { - ...existingShape, - svgContent: this.createSVGElement( - shape, - existingShape.fillColor, - '#333', - 60, - ), - }; - newSelectedShapes.push(updatedShape); - const colorIndex = this.data.basic12Colors.findIndex( - (c) => c.hex === existingShape.fillColor, - ); - if (colorIndex !== -1) { - usedColorIndexes.push(colorIndex); - } - } else { - // 新形状,分配随机颜色 - const randomColor = - this.getRandomUnusedColor(usedColorIndexes); - const newShape = { - id: Date.now() + index, - shapeId: shape.id, - name: shape.name, - fillColor: randomColor.hex, - colorName: randomColor.name, - drawFunction: shape.drawFunction, - svgContent: this.createSVGElement( - shape, - randomColor.hex, - '#333', - 60, - ), - }; - newSelectedShapes.push(newShape); - } - } - }); - - this.setData( - { - selectedShapes: newSelectedShapes, - usedColorIndexes, - showShapeSelector: false, - }, - () => { - // 自动生成新的打印纸 - if (newSelectedShapes.length > 0) { - this.generateImage(); - } - }, - ); - - wx.showToast({ - title: `已选择${newSelectedShapes.length}个形状`, - icon: 'success', - duration: 1500, - }); - }, - - // 获取随机未使用的颜色 - getRandomUnusedColor(usedIndexes) { - const { basic12Colors } = this.data; - const availableIndexes = []; - - for (let i = 0; i < basic12Colors.length; i++) { - if (!usedIndexes.includes(i)) { - availableIndexes.push(i); - } - } - - if (availableIndexes.length === 0) { - // 如果所有颜色都被使用,则重新开始 - const randomIndex = Math.floor( - Math.random() * basic12Colors.length, - ); - usedIndexes.push(randomIndex); - return basic12Colors[randomIndex]; - } - - const randomIndex = - availableIndexes[ - Math.floor(Math.random() * availableIndexes.length) - ]; - usedIndexes.push(randomIndex); - return basic12Colors[randomIndex]; - }, - - // 删除形状 - deleteShape(e) { - const index = parseInt(e.currentTarget.dataset.index); - const selectedShapes = [...this.data.selectedShapes]; - const deletedShape = selectedShapes[index]; - - // 释放颜色 - const colorIndex = this.data.basic12Colors.findIndex( - (c) => c.hex === deletedShape.fillColor, - ); - const usedColorIndexes = this.data.usedColorIndexes.filter( - (i) => i !== colorIndex, - ); - - selectedShapes.splice(index, 1); - - this.setData( - { - selectedShapes, - usedColorIndexes, - }, - () => { - // 如果还有形状,自动重新生成 - if (selectedShapes.length > 0) { - this.generateImage(); - } else { - // 清空生成的图片 - this.setData({ - generatedImage: null, - }); - } - }, - ); - - wx.showToast({ - title: '已删除形状', - icon: 'success', - duration: 1000, - }); - }, - - // 打开颜色选择器 - openColorPicker(e) { - const index = parseInt(e.currentTarget.dataset.index); - const shape = this.data.selectedShapes[index]; - - this.setData({ - showColorPicker: true, - currentEditingIndex: index, - currentEditingColor: shape.fillColor, - }); - }, - - // 关闭颜色选择器 - closeColorPicker() { - this.setData({ - showColorPicker: false, - currentEditingIndex: -1, - currentEditingColor: '', - }); - }, - - // 颜色改变事件 - onColorChange(e) { - const { color } = e.detail; - const { currentEditingIndex, selectedShapes, basic12Colors } = - this.data; - - if (currentEditingIndex >= 0) { - const newSelectedShapes = [...selectedShapes]; - const currentShape = newSelectedShapes[currentEditingIndex]; - const shapeDefinition = this.data.availableShapes.find( - (s) => s.id === currentShape.shapeId, - ); - const colorInfo = basic12Colors.find((c) => c.hex === color) || - WATER_COLORS.extended24.find((c) => c.hex === color) || { - name: '自定义色', - hex: color, - }; - - newSelectedShapes[currentEditingIndex] = { - ...currentShape, - fillColor: color, - colorName: colorInfo.name, - svgContent: this.createSVGElement( - shapeDefinition, - color, - '#333', - 60, - ), - }; - - this.setData( - { - selectedShapes: newSelectedShapes, - showColorPicker: false, - currentEditingIndex: -1, - }, - () => { - // 自动重新生成 - this.generateImage(); - }, - ); - - wx.showToast({ - title: '颜色已更新', - icon: 'success', - duration: 1000, - }); - } - }, - - // 设置背景颜色 - setBackground(e) { - this.setData( - { - bgColor: e.detail.value, - }, - () => { - // 如果有形状,自动重新生成 - if (this.data.selectedShapes.length > 0) { - this.generateImage(); - } - }, - ); - }, - - // 生成练习页图片 - async generateImage() { - if (this.data.selectedShapes.length === 0) { - wx.showToast({ - title: '请先选择形状', - icon: 'none', - }); - return; - } - - this.setData({ isGenerating: true }); - - try { - const canvas = await this.getCanvas(); - const ctx = canvas.getContext('2d'); - - // A4纸张尺寸 (595x842 像素,72 DPI) - const width = 595; - const height = 842; - - // 设置画布尺寸 - canvas.width = width; - canvas.height = height; - - // 设置背景 - ctx.fillStyle = this.data.bgColor; - ctx.fillRect(0, 0, width, height); - - // 绘制标题 - this.drawTitle(ctx, width); - - // 绘制有颜色的示例部分(上半部) - this.drawColoredExamples(ctx, width, height); - - // 绘制分隔线 - this.drawDivider(ctx, width, height); - - // 绘制练习区域(下半部,无颜色) - this.drawPracticeArea(ctx, width, height); - - // 导出图片 - wx.canvasToTempFilePath({ - canvas: canvas, - fileType: 'png', - quality: 1, - success: (res) => { - this.setData({ - generatedImage: res.tempFilePath, - isGenerating: false, - }); - - wx.showToast({ - title: '生成完成', - icon: 'success', - }); - }, - fail: (error) => { - console.error('导出图片失败:', error); - this.setData({ isGenerating: false }); - wx.showToast({ - title: '生成失败,请重试', - icon: 'none', - }); - }, - }); - } catch (error) { - console.error('生成图片失败:', error); - this.setData({ isGenerating: false }); - wx.showToast({ - title: '生成失败,请重试', - icon: 'none', - }); - } - }, - - // 获取Canvas实例 - getCanvas() { - return new Promise((resolve) => { - const query = wx.createSelectorQuery().in(this); - query - .select('#mainCanvas') - .fields({ node: true, size: true }) - .exec((res) => { - const canvas = res[0].node; - resolve(canvas); - }); - }); - }, - - // 绘制标题 - drawTitle(ctx, width) { - ctx.save(); - ctx.fillStyle = '#333'; - ctx.font = 'bold 24px Arial'; - ctx.textAlign = 'center'; - ctx.fillText('形状涂色练习', width / 2, 40); - - ctx.font = '16px Arial'; - ctx.fillText('请为下面的形状涂上颜色', width / 2, 70); - ctx.restore(); - }, - - // 绘制有颜色的示例 - drawColoredExamples(ctx, width, height) { - const { selectedShapes } = this.data; - const startY = 100; - const shapeSize = 80; - const cols = 3; - const spacing = width / (cols + 1); - - selectedShapes.forEach((shape, index) => { - const row = Math.floor(index / cols); - const col = index % cols; - const x = (col + 1) * spacing; - const y = startY + row * (shapeSize + 60); - - // 绘制形状 - this.drawShapeOnCanvas(ctx, shape, x, y, shapeSize, true); - - // 绘制形状名称 - ctx.save(); - ctx.fillStyle = '#333'; - ctx.font = '14px Arial'; - ctx.textAlign = 'center'; - ctx.fillText(shape.name, x, y + shapeSize / 2 + 25); - ctx.restore(); - }); - }, - - // 绘制分隔线 - drawDivider(ctx, width, height) { - const y = height / 2; - ctx.save(); - ctx.strokeStyle = '#ccc'; - ctx.lineWidth = 2; - ctx.setLineDash([10, 5]); - ctx.beginPath(); - ctx.moveTo(50, y); - ctx.lineTo(width - 50, y); - ctx.stroke(); - - // 分隔线文字 - ctx.fillStyle = '#666'; - ctx.font = '14px Arial'; - ctx.textAlign = 'center'; - ctx.fillText('练习区域(请涂色)', width / 2, y + 20); - ctx.restore(); - }, - - // 绘制练习区域 - drawPracticeArea(ctx, width, height) { - const { selectedShapes } = this.data; - const startY = height / 2 + 60; - const shapeSize = 80; - const cols = 3; - const spacing = width / (cols + 1); - - selectedShapes.forEach((shape, index) => { - const row = Math.floor(index / cols); - const col = index % cols; - const x = (col + 1) * spacing; - const y = startY + row * (shapeSize + 60); - - // 绘制形状(无填充色,仅轮廓) - this.drawShapeOnCanvas(ctx, shape, x, y, shapeSize, false); - - // 绘制形状名称 - ctx.save(); - ctx.fillStyle = '#333'; - ctx.font = '14px Arial'; - ctx.textAlign = 'center'; - ctx.fillText(shape.name, x, y + shapeSize / 2 + 25); - ctx.restore(); - }); - }, - - // 在Canvas上绘制形状 - drawShapeOnCanvas(ctx, shape, x, y, size, filled) { - const color = filled ? shape.fillColor : 'transparent'; - - switch (shape.drawFunction) { - case 'drawCircle': - this.drawCircle(ctx, x, y, size, color, '#333', filled); - break; - case 'drawSquare': - this.drawSquare(ctx, x, y, size, color, '#333', filled); - break; - case 'drawRectangle': - this.drawRectangle(ctx, x, y, size, color, '#333', filled); - break; - case 'drawTriangle': - this.drawTriangle(ctx, x, y, size, color, '#333', filled); - break; - case 'drawOval': - this.drawOval(ctx, x, y, size, color, '#333', filled); - break; - case 'drawDiamond': - this.drawDiamond(ctx, x, y, size, color, '#333', filled); - break; - case 'drawStar': - this.drawStar(ctx, x, y, size, color, '#333', filled); - break; - case 'drawHeart': - this.drawHeart(ctx, x, y, size, color, '#333', filled); - break; - case 'drawHexagon': - this.drawHexagon(ctx, x, y, size, color, '#333', filled); - break; - case 'drawPentagon': - this.drawPentagon(ctx, x, y, size, color, '#333', filled); - break; - case 'drawSemicircle': - this.drawSemicircle(ctx, x, y, size, color, '#333', filled); - break; - case 'drawTrapezoid': - this.drawTrapezoid(ctx, x, y, size, color, '#333', filled); - break; - case 'drawCylinder': - this.drawCylinder(ctx, x, y, size, color, '#333', filled); - break; - case 'drawCube': - this.drawCube(ctx, x, y, size, color, '#333', filled); - break; - case 'drawSphere': - this.drawSphere(ctx, x, y, size, color, '#333', filled); - break; - case 'drawCone': - this.drawCone(ctx, x, y, size, color, '#333', filled); - break; - } - }, - - // 绘制各种形状的函数(更新参数以支持边框色) - drawCircle(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - ctx.beginPath(); - ctx.arc(x, y, size * 0.4, 0, Math.PI * 2); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawSquare(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const squareSize = size * 0.8; - const startX = x - squareSize / 2; - const startY = y - squareSize / 2; - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fillRect(startX, startY, squareSize, squareSize); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.strokeRect(startX, startY, squareSize, squareSize); - ctx.restore(); - }, - - drawRectangle(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const rectWidth = size * 0.9; - const rectHeight = size * 0.6; - const startX = x - rectWidth / 2; - const startY = y - rectHeight / 2; - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fillRect(startX, startY, rectWidth, rectHeight); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.strokeRect(startX, startY, rectWidth, rectHeight); - ctx.restore(); - }, - - drawTriangle(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const triangleSize = size * 0.8; - ctx.beginPath(); - ctx.moveTo(x, y - triangleSize / 2); - ctx.lineTo(x - triangleSize / 2, y + triangleSize / 2); - ctx.lineTo(x + triangleSize / 2, y + triangleSize / 2); - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawOval(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - ctx.beginPath(); - ctx.ellipse(x, y, size * 0.45, size * 0.3, 0, 0, Math.PI * 2); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawDiamond(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const diamondSize = size * 0.4; - ctx.beginPath(); - ctx.moveTo(x, y - diamondSize); - ctx.lineTo(x + diamondSize, y); - ctx.lineTo(x, y + diamondSize); - ctx.lineTo(x - diamondSize, y); - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawStar(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const outerRadius = size * 0.4; - const innerRadius = size * 0.2; - ctx.beginPath(); - for (let i = 0; i < 10; i++) { - const angle = (i * Math.PI) / 5; - const radius = i % 2 === 0 ? outerRadius : innerRadius; - const pointX = x + Math.cos(angle - Math.PI / 2) * radius; - const pointY = y + Math.sin(angle - Math.PI / 2) * radius; - if (i === 0) ctx.moveTo(pointX, pointY); - else ctx.lineTo(pointX, pointY); - } - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawHeart(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const heartSize = size * 0.4; - ctx.beginPath(); - ctx.moveTo(x, y + heartSize * 0.3); - ctx.bezierCurveTo( - x - heartSize * 0.5, - y - heartSize * 0.3, - x - heartSize, - y + heartSize * 0.1, - x, - y + heartSize * 0.8, - ); - ctx.bezierCurveTo( - x + heartSize, - y + heartSize * 0.1, - x + heartSize * 0.5, - y - heartSize * 0.3, - x, - y + heartSize * 0.3, - ); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawHexagon(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const radius = size * 0.4; - ctx.beginPath(); - for (let i = 0; i < 6; i++) { - const angle = (i * Math.PI) / 3; - const pointX = x + Math.cos(angle) * radius; - const pointY = y + Math.sin(angle) * radius; - if (i === 0) ctx.moveTo(pointX, pointY); - else ctx.lineTo(pointX, pointY); - } - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawPentagon(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const radius = size * 0.4; - ctx.beginPath(); - for (let i = 0; i < 5; i++) { - const angle = (i * 2 * Math.PI) / 5 - Math.PI / 2; - const pointX = x + Math.cos(angle) * radius; - const pointY = y + Math.sin(angle) * radius; - if (i === 0) ctx.moveTo(pointX, pointY); - else ctx.lineTo(pointX, pointY); - } - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawSemicircle(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const radius = size * 0.4; - ctx.beginPath(); - ctx.arc(x, y, radius, 0, Math.PI); - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - drawTrapezoid(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const trapezoidSize = size * 0.4; - ctx.beginPath(); - ctx.moveTo(x - trapezoidSize * 0.6, y - trapezoidSize * 0.5); - ctx.lineTo(x + trapezoidSize * 0.6, y - trapezoidSize * 0.5); - ctx.lineTo(x + trapezoidSize, y + trapezoidSize * 0.5); - ctx.lineTo(x - trapezoidSize, y + trapezoidSize * 0.5); - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - ctx.restore(); - }, - - // === 3D形状绘制函数 === - - // 绘制圆柱体 - drawCylinder(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const radius = size * 0.3; - const height = size * 0.6; - - // 绘制主体 - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fillRect(x - radius, y - height / 2, radius * 2, height); - } - - // 绘制主体边框 - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.moveTo(x - radius, y - height / 2); - ctx.lineTo(x - radius, y + height / 2); - ctx.moveTo(x + radius, y - height / 2); - ctx.lineTo(x + radius, y + height / 2); - ctx.stroke(); - - // 绘制顶部椭圆 - ctx.beginPath(); - ctx.ellipse(x, y - height / 2, radius, radius * 0.3, 0, 0, Math.PI * 2); - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - ctx.stroke(); - - // 绘制底部椭圆 - ctx.beginPath(); - ctx.ellipse(x, y + height / 2, radius, radius * 0.3, 0, 0, Math.PI * 2); - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - ctx.stroke(); - - ctx.restore(); - }, - - // 绘制正方体 - drawCube(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const cubeSize = size * 0.4; - const offset = cubeSize * 0.3; // 3D偏移量 - - // 绘制后面(右侧面) - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.globalAlpha = 0.6; // 较暗 - ctx.fillRect( - x - cubeSize / 2 + offset, - y - cubeSize / 2 - offset, - cubeSize, - cubeSize, - ); - ctx.globalAlpha = 1; - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 2; - ctx.strokeRect( - x - cubeSize / 2 + offset, - y - cubeSize / 2 - offset, - cubeSize, - cubeSize, - ); - - // 绘制前面(正面) - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fillRect( - x - cubeSize / 2, - y - cubeSize / 2, - cubeSize, - cubeSize, - ); - } - - ctx.strokeRect(x - cubeSize / 2, y - cubeSize / 2, cubeSize, cubeSize); - - // 绘制连接线 - ctx.beginPath(); - // 左上角连接线 - ctx.moveTo(x - cubeSize / 2, y - cubeSize / 2); - ctx.lineTo(x - cubeSize / 2 + offset, y - cubeSize / 2 - offset); - // 右上角连接线 - ctx.moveTo(x + cubeSize / 2, y - cubeSize / 2); - ctx.lineTo(x + cubeSize / 2 + offset, y - cubeSize / 2 - offset); - // 右下角连接线 - ctx.moveTo(x + cubeSize / 2, y + cubeSize / 2); - ctx.lineTo(x + cubeSize / 2 + offset, y + cubeSize / 2 - offset); - ctx.stroke(); - - ctx.restore(); - }, - - // 绘制球体 - drawSphere(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const radius = size * 0.4; - - // 绘制主体圆形 - ctx.beginPath(); - ctx.arc(x, y, radius, 0, Math.PI * 2); - - if (filled && fillColor !== 'transparent') { - // 创建径向渐变模拟3D效果 - const gradient = ctx.createRadialGradient( - x - radius * 0.3, - y - radius * 0.3, - 0, - x, - y, - radius, - ); - gradient.addColorStop(0, '#ffffff'); - gradient.addColorStop(0.3, fillColor); - gradient.addColorStop(1, '#000000'); - ctx.fillStyle = gradient; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - - // 绘制纬线 - ctx.strokeStyle = borderColor; - ctx.lineWidth = 1; - ctx.globalAlpha = 0.5; - - // 水平纬线 - ctx.beginPath(); - ctx.ellipse(x, y, radius, radius * 0.3, 0, 0, Math.PI * 2); - ctx.stroke(); - - // 垂直经线 - ctx.beginPath(); - ctx.ellipse(x, y, radius * 0.3, radius, 0, 0, Math.PI * 2); - ctx.stroke(); - - ctx.restore(); - }, - - // 绘制圆锥体 - drawCone(ctx, x, y, size, fillColor, borderColor, filled = true) { - ctx.save(); - const radius = size * 0.3; - const height = size * 0.6; - - // 绘制圆锥主体 - ctx.beginPath(); - ctx.moveTo(x, y - height / 2); // 顶点 - ctx.lineTo(x - radius, y + height / 2); // 左下 - ctx.lineTo(x + radius, y + height / 2); // 右下 - ctx.closePath(); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.strokeStyle = borderColor; - ctx.lineWidth = 3; - ctx.stroke(); - - // 绘制底部椭圆 - ctx.beginPath(); - ctx.ellipse(x, y + height / 2, radius, radius * 0.3, 0, 0, Math.PI * 2); - - if (filled && fillColor !== 'transparent') { - ctx.fillStyle = fillColor; - ctx.fill(); - } - - ctx.stroke(); - - ctx.restore(); - }, - - // 保存到相册 - saveToAlbum() { - if (!this.data.generatedImage) { - wx.showToast({ - title: '没有可保存的图片', - icon: 'none', - }); - return; - } - - wx.saveImageToPhotosAlbum({ - filePath: this.data.generatedImage, - success: () => { - wx.showToast({ - title: '已保存到相册', - icon: 'success', - }); - }, - fail: (err) => { - console.error('保存失败:', err); - if (err.errMsg.includes('auth')) { - wx.showModal({ - title: '提示', - content: '需要授权访问相册才能保存图片', - confirmText: '去授权', - success: (res) => { - if (res.confirm) { - wx.openSetting(); - } - }, - }); - } else { - wx.showToast({ - title: '保存失败', - icon: 'none', - }); - } - }, - }); - }, - - // 预览图片 - previewImage() { - if (this.data.generatedImage) { - wx.previewImage({ - urls: [this.data.generatedImage], - current: this.data.generatedImage, - }); - } - }, -}); diff --git a/miniprogram/demoPages/shapePrint/index.json b/miniprogram/demoPages/shapePrint/index.json deleted file mode 100644 index e425a53..0000000 --- a/miniprogram/demoPages/shapePrint/index.json +++ /dev/null @@ -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" - } -} diff --git a/miniprogram/demoPages/shapePrint/index.less b/miniprogram/demoPages/shapePrint/index.less deleted file mode 100644 index d123c37..0000000 --- a/miniprogram/demoPages/shapePrint/index.less +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/miniprogram/demoPages/shapePrint/index.wxml b/miniprogram/demoPages/shapePrint/index.wxml deleted file mode 100644 index f101257..0000000 --- a/miniprogram/demoPages/shapePrint/index.wxml +++ /dev/null @@ -1,171 +0,0 @@ - - - - 形状涂色练习生成器 - 为小朋友制作涂色练习页 - - - - - - - - - - 已选择的形状 ({{selectedShapes.length}}) - - - - - - - - - - - - - {{item.name}} - - - - {{item.colorName}} - - - - - - - - 打印设置 - - 背景颜色: - - - - - - - - - - - - - - - - 生成的练习页 - - 点击图片可放大预览 • A4尺寸 • 适合打印 - - - - - - - 选择形状 (最多6个) - 已选: {{selectedShapes.length}}/6 - - - - - - - - - - - - - - {{item.name}} - - - - - - - - - - - - - - - - diff --git a/miniprogram/demoPages/shapePrint/shapes.js b/miniprogram/demoPages/shapePrint/shapes.js deleted file mode 100644 index 61316e0..0000000 --- a/miniprogram/demoPages/shapePrint/shapes.js +++ /dev/null @@ -1,134 +0,0 @@ -// shapes.js - 幼儿园到小学三年级基本几何形状定义 -const shapes = [ - { - id: 'circle', - name: '圆形', - type: 'circle', - svg: '', - drawFunction: 'drawCircle', - }, - { - id: 'square', - name: '正方形', - type: 'rect', - svg: '', - drawFunction: 'drawSquare', - }, - { - id: 'rectangle', - name: '长方形', - type: 'rect', - svg: '', - drawFunction: 'drawRectangle', - }, - { - id: 'triangle', - name: '三角形', - type: 'triangle', - svg: '', - drawFunction: 'drawTriangle', - }, - { - id: 'oval', - name: '椭圆形', - type: 'oval', - svg: '', - drawFunction: 'drawOval', - }, - { - id: 'diamond', - name: '菱形', - type: 'diamond', - svg: '', - drawFunction: 'drawDiamond', - }, - { - id: 'star', - name: '星形', - type: 'star', - svg: '', - drawFunction: 'drawStar', - }, - { - id: 'heart', - name: '心形', - type: 'heart', - svg: '', - drawFunction: 'drawHeart', - }, - { - id: 'hexagon', - name: '六边形', - type: 'hexagon', - svg: '', - drawFunction: 'drawHexagon', - }, - { - id: 'pentagon', - name: '五边形', - type: 'pentagon', - svg: '', - drawFunction: 'drawPentagon', - }, - { - id: 'semicircle', - name: '半圆形', - type: 'semicircle', - svg: '', - drawFunction: 'drawSemicircle', - }, - { - id: 'trapezoid', - name: '梯形', - type: 'trapezoid', - svg: '', - drawFunction: 'drawTrapezoid', - }, - // 新增3D形状 - { - id: 'cylinder', - name: '圆柱体', - type: '3d-cylinder', - svg: '', - drawFunction: 'drawCylinder', - }, - { - id: 'cube', - name: '正方体', - type: '3d-cube', - svg: '', - drawFunction: 'drawCube', - }, - { - id: 'sphere', - name: '球体', - type: '3d-sphere', - svg: '', - drawFunction: 'drawSphere', - }, - { - id: 'cone', - name: '圆锥体', - type: '3d-cone', - svg: '', - 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 }; diff --git a/miniprogram/demoPages/textPaint/textPaint.js b/miniprogram/demoPages/textPaint/textPaint.js deleted file mode 100644 index ddd7a59..0000000 --- a/miniprogram/demoPages/textPaint/textPaint.js +++ /dev/null @@ -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(); - }, -}); diff --git a/miniprogram/demoPages/textPaint/textPaint.json b/miniprogram/demoPages/textPaint/textPaint.json deleted file mode 100644 index 24d9d2c..0000000 --- a/miniprogram/demoPages/textPaint/textPaint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "navigationBarTitleText": "文本绘制 示例", - "navigationBarBackgroundColor": "#d2e7d8", - "homeButton": true, - "backgroundColor": "#e8eddb", - "enablePullDownRefresh": false -} diff --git a/miniprogram/demoPages/textPaint/textPaint.less b/miniprogram/demoPages/textPaint/textPaint.less deleted file mode 100644 index f7df8c2..0000000 --- a/miniprogram/demoPages/textPaint/textPaint.less +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/miniprogram/demoPages/textPaint/textPaint.ts b/miniprogram/demoPages/textPaint/textPaint.ts deleted file mode 100644 index bc78028..0000000 --- a/miniprogram/demoPages/textPaint/textPaint.ts +++ /dev/null @@ -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(); - }, -}); diff --git a/miniprogram/demoPages/textPaint/textPaint.wxml b/miniprogram/demoPages/textPaint/textPaint.wxml deleted file mode 100644 index c1f49ae..0000000 --- a/miniprogram/demoPages/textPaint/textPaint.wxml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/miniprogram/demoPages/textPaint/textPaint.wxss b/miniprogram/demoPages/textPaint/textPaint.wxss deleted file mode 100644 index d245ef4..0000000 --- a/miniprogram/demoPages/textPaint/textPaint.wxss +++ /dev/null @@ -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%; -} diff --git a/miniprogram/demoPages/textPaint2/textPaint.json b/miniprogram/demoPages/textPaint2/textPaint.json deleted file mode 100644 index bb96f80..0000000 --- a/miniprogram/demoPages/textPaint2/textPaint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "navigationBarTitleText": "文本绘制 示例", - "navigationBarBackgroundColor": "#798154", - "homeButton": false, - "backgroundColorContent": "#ffffff00", - "enablePullDownRefresh": false -} diff --git a/miniprogram/demoPages/textPaint2/textPaint.less b/miniprogram/demoPages/textPaint2/textPaint.less deleted file mode 100644 index 7737f19..0000000 --- a/miniprogram/demoPages/textPaint2/textPaint.less +++ /dev/null @@ -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; -} diff --git a/miniprogram/demoPages/textPaint2/textPaint.ts b/miniprogram/demoPages/textPaint2/textPaint.ts deleted file mode 100644 index c9a5ed4..0000000 --- a/miniprogram/demoPages/textPaint2/textPaint.ts +++ /dev/null @@ -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, - 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; - } - }, -}); diff --git a/miniprogram/demoPages/textPaint2/textPaint.wxml b/miniprogram/demoPages/textPaint2/textPaint.wxml deleted file mode 100644 index cf16984..0000000 --- a/miniprogram/demoPages/textPaint2/textPaint.wxml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - 当前宽度:{{boxWidth}}px,高度:{{boxHeight}}px - - - \ No newline at end of file diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-1.png b/miniprogram/mathPages/assets/animal-number/animal-number-1.png new file mode 100644 index 0000000..fa2a4e3 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-1.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-10.png b/miniprogram/mathPages/assets/animal-number/animal-number-10.png new file mode 100644 index 0000000..7ce9018 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-10.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-2.png b/miniprogram/mathPages/assets/animal-number/animal-number-2.png new file mode 100644 index 0000000..14c0aa1 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-2.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-3.png b/miniprogram/mathPages/assets/animal-number/animal-number-3.png new file mode 100644 index 0000000..685f9f5 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-3.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-4.png b/miniprogram/mathPages/assets/animal-number/animal-number-4.png new file mode 100644 index 0000000..b695473 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-4.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-5.png b/miniprogram/mathPages/assets/animal-number/animal-number-5.png new file mode 100644 index 0000000..a454876 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-5.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-6.png b/miniprogram/mathPages/assets/animal-number/animal-number-6.png new file mode 100644 index 0000000..0fbde14 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-6.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-7.png b/miniprogram/mathPages/assets/animal-number/animal-number-7.png new file mode 100644 index 0000000..78b6305 Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-7.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-8.png b/miniprogram/mathPages/assets/animal-number/animal-number-8.png new file mode 100644 index 0000000..89d8abe Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-8.png differ diff --git a/miniprogram/mathPages/assets/animal-number/animal-number-9.png b/miniprogram/mathPages/assets/animal-number/animal-number-9.png new file mode 100644 index 0000000..2cf166c Binary files /dev/null and b/miniprogram/mathPages/assets/animal-number/animal-number-9.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-1.png b/miniprogram/mathPages/assets/finger/finger-1.png new file mode 100644 index 0000000..7989b4d Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-1.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-10.png b/miniprogram/mathPages/assets/finger/finger-10.png new file mode 100644 index 0000000..36df6a1 Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-10.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-2.png b/miniprogram/mathPages/assets/finger/finger-2.png new file mode 100644 index 0000000..5b708e4 Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-2.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-3.png b/miniprogram/mathPages/assets/finger/finger-3.png new file mode 100644 index 0000000..0acc6b4 Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-3.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-4.png b/miniprogram/mathPages/assets/finger/finger-4.png new file mode 100644 index 0000000..7a05898 Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-4.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-5.png b/miniprogram/mathPages/assets/finger/finger-5.png new file mode 100644 index 0000000..712d523 Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-5.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-6.png b/miniprogram/mathPages/assets/finger/finger-6.png new file mode 100644 index 0000000..f6d29a0 Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-6.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-7.png b/miniprogram/mathPages/assets/finger/finger-7.png new file mode 100644 index 0000000..3e43b29 Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-7.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-8.png b/miniprogram/mathPages/assets/finger/finger-8.png new file mode 100644 index 0000000..52260bf Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-8.png differ diff --git a/miniprogram/mathPages/assets/finger/finger-9.png b/miniprogram/mathPages/assets/finger/finger-9.png new file mode 100644 index 0000000..4df9fac Binary files /dev/null and b/miniprogram/mathPages/assets/finger/finger-9.png differ diff --git a/miniprogram/mathPages/numberColor/numberColor.json b/miniprogram/mathPages/numberColor/numberColor.json new file mode 100644 index 0000000..3cb0fe5 --- /dev/null +++ b/miniprogram/mathPages/numberColor/numberColor.json @@ -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" + } +} diff --git a/miniprogram/mathPages/numberColor/numberColor.less b/miniprogram/mathPages/numberColor/numberColor.less new file mode 100644 index 0000000..3847f10 --- /dev/null +++ b/miniprogram/mathPages/numberColor/numberColor.less @@ -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; +} \ No newline at end of file diff --git a/miniprogram/mathPages/numberColor/numberColor.ts b/miniprogram/mathPages/numberColor/numberColor.ts new file mode 100644 index 0000000..cbea790 --- /dev/null +++ b/miniprogram/mathPages/numberColor/numberColor.ts @@ -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 = { + '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); + } + }, +}); diff --git a/miniprogram/mathPages/numberColor/numberColor.wxml b/miniprogram/mathPages/numberColor/numberColor.wxml new file mode 100644 index 0000000..af0849d --- /dev/null +++ b/miniprogram/mathPages/numberColor/numberColor.wxml @@ -0,0 +1,94 @@ + + + 预览打印效果 + + + + + + + + + + + + {{number}} + + + + + + + {{number}} + + + + + + + {{number}} + + + + + + 随机 + + + + + + + + + 分享 + + + 下载打印 + + + diff --git a/miniprogram/pages/buttonTest/buttonTest.json b/miniprogram/pages/buttonTest/buttonTest.json deleted file mode 100644 index ae35187..0000000 --- a/miniprogram/pages/buttonTest/buttonTest.json +++ /dev/null @@ -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" - } -} diff --git a/miniprogram/pages/buttonTest/buttonTest.less b/miniprogram/pages/buttonTest/buttonTest.less deleted file mode 100644 index d9f9016..0000000 --- a/miniprogram/pages/buttonTest/buttonTest.less +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/miniprogram/pages/buttonTest/buttonTest.ts b/miniprogram/pages/buttonTest/buttonTest.ts deleted file mode 100644 index 9df8451..0000000 --- a/miniprogram/pages/buttonTest/buttonTest.ts +++ /dev/null @@ -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', - }); - } -}); diff --git a/miniprogram/pages/buttonTest/buttonTest.wxml b/miniprogram/pages/buttonTest/buttonTest.wxml deleted file mode 100644 index 8331d2b..0000000 --- a/miniprogram/pages/buttonTest/buttonTest.wxml +++ /dev/null @@ -1,160 +0,0 @@ - - - 主要按钮 (Primary) - - 主要按钮 - 禁用状态 - 加载中 - - - - - 次要按钮 (Secondary) - - 次要按钮 - 禁用状态 - 加载中 - - - - - 绿色按钮 (Green) - - 绿色按钮 - 禁用状态 - 加载中 - - - - - 白色按钮 (White) - - 白色按钮 - 禁用状态 - 透明模式 - - - - - 扁平化按钮 (Flat) - - 扁平主要 - 扁平次要 - 扁平白色 - - - - - 带图标按钮 - - 添加 - 删除 - 编辑 - - - - - 不同尺寸 - - - 小按钮 - - 中按钮 - - 大按钮 - - - - - - - 点击动效测试 - - 点击我测试动效 - 抖动效果 - 缩放动画 - - - - - Word Card 测试 - - - - - - - - - - - diff --git a/miniprogram/pages/mathIndex/mathIndex.json b/miniprogram/pages/mathIndex/mathIndex.json new file mode 100644 index 0000000..96d223c --- /dev/null +++ b/miniprogram/pages/mathIndex/mathIndex.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "涂鸦丫-数学", + "navigationBarBackgroundColor": "#FFD719", + "homeButton": true, + "backgroundColor": "#F6F6F6", + "enablePullDownRefresh": false, + "usingComponents": {} +} diff --git a/miniprogram/pages/mathIndex/mathIndex.less b/miniprogram/pages/mathIndex/mathIndex.less new file mode 100644 index 0000000..5704ded --- /dev/null +++ b/miniprogram/pages/mathIndex/mathIndex.less @@ -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; +} \ No newline at end of file diff --git a/miniprogram/pages/mathIndex/mathIndex.ts b/miniprogram/pages/mathIndex/mathIndex.ts new file mode 100644 index 0000000..823c361 --- /dev/null +++ b/miniprogram/pages/mathIndex/mathIndex.ts @@ -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, + }); + } + }, +}); diff --git a/miniprogram/pages/mathIndex/mathIndex.wxml b/miniprogram/pages/mathIndex/mathIndex.wxml new file mode 100644 index 0000000..e072c78 --- /dev/null +++ b/miniprogram/pages/mathIndex/mathIndex.wxml @@ -0,0 +1,42 @@ + + + + 数学学习涂鸦卡 + 选择功能,生成可打印的数学学习涂鸦卡 + + + + + + + {{item.icon}} + + + {{item.title}} + {{item.desc}} + + + + + + + + 💡 + + 使用提示 + + 1. 点击上方功能卡片选择要生成的涂鸦卡类型\n 2. + 选择数字范围、图形类型等参数\n 3. 预览生成的涂鸦卡效果\n 4. + 下载A4格式PNG图片,打印后使用 + + + + + diff --git a/project.private.config.json b/project.private.config.json index 84a222f..e54748e 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -23,12 +23,26 @@ "condition": { "miniprogram": { "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", "pathName": "pages/copyBook/copyBook", "query": "", - "scene": null, - "launchMode": "default" + "launchMode": "default", + "scene": null }, { "name": "pages/buttonTest/buttonTest",