feat:数字首页

This commit is contained in:
R524809
2025-11-25 17:52:18 +08:00
parent ec773a650c
commit 61e7781bf8
56 changed files with 827 additions and 3403 deletions
+14 -3
View File
@@ -2,10 +2,15 @@
"pages": [ "pages": [
"pages/index/index", "pages/index/index",
"pages/shape/index", "pages/shape/index",
"pages/debug/debug", "pages/mathIndex/mathIndex",
"pages/wordDemo/index",
"pages/copyBook/copyBook", "pages/copyBook/copyBook",
"pages/buttonTest/buttonTest" "pages/debug/debug"
],
"subPackages": [
{
"root": "mathPages",
"pages": ["numberColor/numberColor"]
}
], ],
"window": {}, "window": {},
"style": "v2", "style": "v2",
@@ -37,6 +42,12 @@
"selectedIconPath": "assets/tabBar/icon-word-active.png", "selectedIconPath": "assets/tabBar/icon-word-active.png",
"text": "识字" "text": "识字"
}, },
{
"pagePath": "pages/mathIndex/mathIndex",
"iconPath": "assets/tabBar/icon-math.png",
"selectedIconPath": "assets/tabBar/icon-math-active.png",
"text": "数学"
},
{ {
"pagePath": "pages/copyBook/copyBook", "pagePath": "pages/copyBook/copyBook",
"iconPath": "assets/tabBar/icon-edit.png", "iconPath": "assets/tabBar/icon-edit.png",
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

-26
View File
@@ -1,26 +0,0 @@
const PAPER_SIZE = {
A4: {
// width: 2480,
// height: 3508,
width: 595,
height: 842,
},
A3: {
width: 3508,
height: 4960,
},
A2: {
width: 4960,
height: 7016,
},
A1: {
width: 7016,
height: 10032,
},
A0: {
width: 10032,
height: 14176,
},
};
export { PAPER_SIZE };
@@ -1,125 +0,0 @@
import { PAPER_SIZE } from './constant';
import { getMiniCodeImagePath } from './utils';
class TextDrawService {
canvas: WechatMiniprogram.Canvas;
ctx: RenderingContext;
options: Record<string, any>;
paperSize: PaperSize;
currentX: number;
currentY: number;
constructor(canvas: Canvas, ctx: RenderingContext, options?: Record<string, any>) {
this.canvas = canvas;
this.ctx = ctx;
this.paperSize = 'A4';
this.options = {
appName: '涂鸦呀',
appHint: '涂色|识字|画画|认知',
title: '找一找 涂 色',
subTitle: '把相同的文字涂上相同的颜色',
radius: 80,
colors: ['#2196F3', '#FFEB3B', '#FF9800', '#F44336'],
characters: ['鱼', '鸟', '猫', '狗'],
...options,
};
this.currentX = 0;
this.currentY = 0;
}
draw() {
this.setPaper();
this.drawHeader();
this.drawLegend();
}
drawHeader() {
const { canvas, ctx } = this;
const { title } = this.options;
ctx.font = 'bold 80px "Microsoft Yahei"';
ctx.fillStyle = '#333';
ctx.textAlign = 'center';
const titleX = canvas.width / 2;
const titleY = 120;
ctx.fillText(title, titleX, titleY);
// 记录当前X、Y坐标
this.currentX = titleX;
this.currentY = titleY + 50;
// 绘制分割线
this.drawLine(this.currentY);
}
// 绘制示例
drawLegend() {
const { ctx, options } = this;
const { colors, characters, radius } = options;
const startX = (this.canvas.width - colors.length * 400) / 2; // 计算起始X坐标以居中
this.currentY = this.currentY + radius + 50; // radius 为圆的半径,再加上离线的边距为50
const startY = this.currentY; // 设置起始Y坐标
ctx.moveTo(startX, startY);
colors.forEach((color: string, index: number) => {
const x = startX + index * 500; // 计算每个圆的X坐标
const y = startY; // 计算圆的Y坐标
// 绘制圆
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
ctx.fillStyle = '#333';
ctx.stroke();
ctx.closePath();
// 绘制长方形
ctx.fillStyle = '#fff'; // 长方形背景色
ctx.strokeStyle = '#333';
ctx.lineWidth = 4;
const rectangleX = x - 120; // 计算字符X坐标
const rectangleY = y + radius + 40; // 计算字符Y坐标
ctx.strokeRect(rectangleX, rectangleY, 240, 100); // 绘制长方形
// ctx.fillRect(x - 75, y + 10, 240, 100); // 填充长方形
// 绘制字符
ctx.fillStyle = '#333';
ctx.font = 'bold 60px "Microsoft Yahei"';
ctx.textAlign = 'center';
ctx.fillText(characters[index], rectangleX + 120, rectangleY + 70);
this.currentY = rectangleY + 100; // 100会矩形宽度
});
this.currentY = this.currentY + 50; // 计算分割线的Y坐标,距离示例图20px
this.drawLine(this.currentY);
}
drawLine(linY: number) {
const { canvas, ctx } = this;
ctx.strokeStyle = '#333';
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(20, linY);
ctx.lineTo(canvas.width - 20, linY);
ctx.stroke();
}
setPaper() {
const ctx = this.ctx;
const { width, height } = PAPER_SIZE[this.paperSize];
this.canvas.width = width;
this.canvas.height = height;
this.clear();
ctx.fillStyle = '#fff'; // 设置背景色为白色
ctx.fillRect(0, 0, width, height);
}
clear() {
const canvas = this.canvas;
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
export default TextDrawService;
@@ -1,237 +0,0 @@
// import { PAPER_SIZE } from './constant';
import { getMiniCodeImage } from './utils';
class TextDrawService {
canvas: WechatMiniprogram.Canvas;
ctx: RenderingContext;
options: Record<string, any>;
paperSize: PaperSize;
currentX: number;
currentY: number;
constructor(canvas: Canvas, ctx: RenderingContext, options?: Record<string, any>) {
this.canvas = canvas;
this.ctx = ctx;
this.paperSize = 'A4';
this.options = {
appName: '涂鸦呀',
appHint: '涂色|识字|画画|认知',
title: '找一找 涂 色',
subTitle: '把相同的文字涂上相同的颜色',
radius: 36,
colors: ['#2196F3', '#FFEB3B', '#FF9800', '#F44336', '#F44336'],
characters: ['鱼', '鸟', '猫', '狗', '牛'],
...options,
};
this.currentX = 0;
this.currentY = 0;
}
draw() {
this.setPaper();
// this.setPaper3();
this.drawHeader();
// this.drawLegend();
}
async drawHeader() {
const { canvas, ctx } = this;
const { appName, appHint, title, subTitle } = this.options;
this.currentX = 60;
this.currentY = 60;
const titleX = this.currentX + 140 + 14;
const titleY = 87;
const image = await getMiniCodeImage(canvas);
ctx.drawImage(image, 60, 60, 140, 140);
ctx.font = 'bold 36px "Microsoft Yahei"';
ctx.fillStyle = '#000';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText(appName, titleX, titleY);
ctx.font = '24px "Microsoft Yahei"';
ctx.fillStyle = '#666';
ctx.fillText(appHint, titleX, 163);
// 绘制分割线
ctx.strokeStyle = '#888';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(483, 70);
ctx.lineTo(483, 70 + 140);
ctx.stroke();
ctx.font = 'bold 36px "Microsoft Yahei"';
ctx.fillStyle = '#000';
ctx.fillText(title, 538, titleY);
ctx.font = '24px "Microsoft Yahei"';
ctx.fillStyle = '#666';
ctx.fillText(subTitle, 538, 163);
this.drawLegend();
}
/** 绘制示例
* 矩形: x、y为左上角
* 圆形:x、y为圆心
* 文字:x textAlign 为 start,文本左边缘对齐
* y textBaseline 为 alphabeticy 对应字母基线(类似左下角)
*
* */
drawLegend() {
const { canvas, ctx, options } = this;
const { colors, characters, radius } = options;
const rectWidth = 100;
const rectHeight = 48;
const startX = 978;
const startY = 60 + radius; // 设置起始Y坐标
const len = characters.length || 4;
const canvasWidth = canvas.width;
/** 计算每个圆之间的间距 俩个60,一个是页面右侧边距,另一个是圆离右边距地边距*/
const spaceWidth = (canvasWidth - startX - 60 - 60 - radius * (len - 1)) / (len - 1);
ctx.moveTo(startX, startY);
colors.forEach((color: string, index: number) => {
const x = startX + index * (spaceWidth + radius);
const y = startY; // 计算圆的Y坐标
// 绘制圆
ctx.strokeStyle = '#333';
ctx.fillStyle = color;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.closePath();
// 绘制长方形
ctx.fillStyle = '#fff'; // 长方形背景色
ctx.strokeStyle = '#333';
ctx.lineWidth = 2;
const rectangleX = x - rectWidth / 2; // 从圆形移动一半的长方形的长
const rectangleY = y + radius + 18; // 从圆形下移一个半径,再加上18的间距
ctx.strokeRect(rectangleX, rectangleY, rectWidth, rectHeight); // 绘制长方形
// 绘制字符
ctx.fillStyle = '#333';
ctx.font = 'bold 24px "Microsoft Yahei"';
ctx.textBaseline = 'top';
ctx.fillText(characters[index], rectangleX + 38, 163);
});
this.currentY = 220; // 计算分割线的Y坐标,距离示例图20px
this.drawLine(this.currentY);
}
drawLine(linY: number) {
const { canvas, ctx } = this;
ctx.strokeStyle = '#333';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(20, linY);
ctx.lineTo(canvas.width - 20, linY);
ctx.stroke();
}
setPaper() {
const { ctx, canvas } = this;
const { pixelRatio: dpr } = wx.getWindowInfo();
const { width, height } = { width: 595 * dpr, height: 842 * dpr };
canvas.width = width;
canvas.height = height;
this.clear();
ctx.fillStyle = '#fff'; // 设置背景色为白色
ctx.fillRect(0, 0, width, height);
}
setPaper3() {
const { ctx, canvas } = this;
const { pixelRatio: dpr } = wx.getWindowInfo();
// @ts-ignore
const winWidth = canvas._width;
// @ts-ignore
const winHeight = canvas._height; // 容器逻辑尺寸
// 计算适配后的物理尺寸
const targetWidth = winWidth * dpr; // 宽度100%适配容器
const aspectRatio = 842 / 595; // 原图宽高比(示例为A4纸比例)
let targetHeight = targetWidth * aspectRatio; // 按比例计算高度
// 判断是否需要裁剪高度
if (targetHeight > winHeight * dpr) {
targetHeight = winHeight * dpr; // 若超出容器高度则强制裁剪
}
// 设置 Canvas 物理像素
canvas.width = targetWidth;
canvas.height = targetHeight;
console.log('targetWidth---:', targetWidth);
console.log('targetHeight---:', targetHeight);
this.clear();
ctx.fillStyle = '#fff'; // 设置背景色为白色
ctx.fillRect(0, 0, targetWidth, targetHeight);
}
setPaper2() {
const { canvas, ctx } = this;
const { pixelRatio: dpr } = wx.getWindowInfo();
const { width, height } = { width: 595 * dpr, height: 842 * dpr };
// @ts-ignore
const winWidth = canvas._width;
// @ts-ignore
const winHeight = canvas._height;
const canvasAspect = width / height; // Canvas 内容的宽高比
const containerAspect = winHeight / winWidth; // 容器的宽高比
// 选择缩放比例较小的方向(确保内容完全显示)
let scaleRatio =
containerAspect > canvasAspect
? winWidth / width // 以宽度为基准
: winHeight / height; // 以高度为基准
console.log('scaleRatio-----:', scaleRatio);
const scaledWidth = width * scaleRatio;
const scaledHeight = height * scaleRatio;
// 避免超过小程序画布最大限制(4096)
if (scaledWidth > 4096 || scaledHeight > 4096) {
scaleRatio *= Math.min(4096 / scaledWidth, 4096 / scaledHeight);
}
console.log('scaledWidth-----:', scaledWidth);
console.log('scaledHeight-----:', scaledHeight);
canvas.width = scaledWidth;
canvas.height = scaledHeight;
ctx.translate((winWidth * dpr) / 2, (winHeight * dpr) / 2);
ctx.scale(scaleRatio * dpr, scaleRatio * dpr);
ctx.translate(-width / 2, -height / 2); // 将内容原点移至中心
// this.canvas.width = width;
// this.canvas.height = height;
this.clear();
ctx.fillStyle = '#fff'; // 设置背景色为白色
ctx.fillRect(0, 0, width, height);
}
clear() {
const canvas = this.canvas;
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
export default TextDrawService;
@@ -1,224 +0,0 @@
// import { PAPER_SIZE } from './constant';
import { PAPER_SIZE } from './constant';
import { getMiniCodeImage } from './utils';
class TextDrawService {
canvas: WechatMiniprogram.Canvas;
ctx: RenderingContext;
options: Record<string, any>;
paperSize: PaperSize;
currentX: number;
currentY: number;
constructor(
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) {
this.canvas = canvas;
this.ctx = ctx;
this.paperSize = 'A4';
this.options = {
appName: '涂鸦呀',
appHint: '涂色|识字|画画|认知',
title: '找一找 涂 色',
subTitle: '把相同的文字涂上相同的颜色',
colors: ['#FF0000', '#FDAF1A', '#0057FC', '#09FF00', '#8D00FF'],
characters: ['文', '升', '国', '舟', '丹'],
...options,
};
this.currentX = 0;
this.currentY = 0;
}
draw() {
this.setPaper();
this.drawHeader();
this.drawLegend();
this.drawContent();
}
async drawHeader() {
const { canvas, ctx } = this;
const { appName, appHint, title, subTitle } = this.options;
this.currentX = 80;
this.currentY = 80;
const titleX = this.currentX + 200 + 48;
const titleY = 80;
const image = await getMiniCodeImage(canvas);
ctx.drawImage(image, 80, 80, 200, 200);
ctx.font = 'bold 64px "Microsoft Yahei"';
ctx.fillStyle = '#000';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText(appName, titleX, titleY);
ctx.font = '48px "Microsoft Yahei"';
ctx.fillStyle = '#666';
ctx.fillText(appHint, titleX, 186);
// 绘制分割线
// ctx.strokeStyle = '#888';
// ctx.lineWidth = 2;
// ctx.beginPath();
// ctx.moveTo(483, 70);
// ctx.lineTo(483, 70 + 140);
// ctx.stroke();
ctx.font = 'bold 64px "Microsoft Yahei"';
ctx.fillStyle = '#000';
ctx.fillText(title, 1015, titleY);
ctx.font = '48px "Microsoft Yahei"';
ctx.fillStyle = '#666';
ctx.fillText(subTitle, 1015, 186);
this.currentY = 304;
this.drawLine(this.currentY);
}
/** 绘制示例
* 矩形: x、y为左上角
* 圆形:x、y为圆心
* 文字:x textAlign 为 start,文本左边缘对齐
* y textBaseline 为 alphabeticy 对应字母基线(类似左下角)
*
* */
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;
-34
View File
@@ -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;
});
}
File diff suppressed because it is too large Load Diff
@@ -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"
}
}
-537
View File
@@ -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;
}
-171
View File
@@ -1,171 +0,0 @@
<view class="container">
<!-- 标题 -->
<view class="header">
<text class="title">形状涂色练习生成器</text>
<text class="subtitle">为小朋友制作涂色练习页</text>
</view>
<!-- 选择形状按钮 -->
<view class="section">
<button
class="select-shapes-btn"
bindtap="openShapeSelector"
type="primary">
<van-icon name="add-o" color="white" size="20" />
<text class="btn-text">选择形状 ({{selectedShapes.length}}/6)</text>
</button>
</view>
<!-- 已选择的形状卡片 -->
<view class="section" wx:if="{{selectedShapes.length > 0}}">
<view class="section-title"
>已选择的形状 ({{selectedShapes.length}})</view
>
<view class="shape-cards-container">
<view
wx:for="{{selectedShapes}}"
wx:key="id"
class="shape-card"
bindtap="openColorPicker"
data-index="{{index}}">
<!-- 删除按钮 -->
<view
class="delete-btn"
catchtap="deleteShape"
data-index="{{index}}">
<van-icon name="close" color="#ff4757" size="16" />
</view>
<!-- 形状预览 -->
<view class="shape-preview-container">
<image
src="{{item.svgContent}}"
class="shape-svg"
mode="aspectFit">
</image>
</view>
<!-- 形状名称 -->
<text class="shape-name">{{item.name}}</text>
<!-- 颜色信息 -->
<view class="color-info">
<view
class="color-dot"
style="background-color: {{item.fillColor}};"></view>
<text class="color-text">{{item.colorName}}</text>
</view>
</view>
</view>
</view>
<!-- 设置区域 -->
<view class="section" wx:if="{{selectedShapes.length > 0}}">
<view class="section-title">打印设置</view>
<view class="setting-item">
<text class="setting-label">背景颜色:</text>
<radio-group bindchange="setBackground" class="bg-radio-group">
<label
wx:for="{{backgrounds}}"
wx:key="value"
class="bg-option">
<radio
value="{{item.value}}"
checked="{{bgColor === item.value}}"
color="#1976d2" />
<text>{{item.name}}</text>
</label>
</radio-group>
</view>
</view>
<!-- 生成按钮 -->
<view class="section" wx:if="{{selectedShapes.length > 0}}">
<button
class="generate-btn"
bindtap="generateImage"
type="primary"
loading="{{isGenerating}}"
disabled="{{selectedShapes.length === 0}}">
{{isGenerating ? '生成中...' : '生成练习页'}}
</button>
<button
wx:if="{{generatedImage}}"
class="save-btn"
bindtap="saveToAlbum"
type="warn">
保存到相册
</button>
</view>
<!-- 预览生成的图片 -->
<view class="section" wx:if="{{generatedImage}}">
<view class="section-title">生成的练习页</view>
<image
src="{{generatedImage}}"
class="preview-image"
mode="aspectFit"
bindtap="previewImage" />
<text class="preview-hint">点击图片可放大预览 • A4尺寸 • 适合打印</text>
</view>
<!-- 形状选择弹窗 -->
<van-popup
show="{{showShapeSelector}}"
bind:close="closeShapeSelector"
custom-class="shape-selector-popup"
position="bottom"
round>
<view class="shape-selector-container">
<view class="popup-header">
<text class="popup-title">选择形状 (最多6个)</text>
<view class="selected-count"
>已选: {{selectedShapes.length}}/6</view
>
</view>
<view class="shape-grid">
<view
wx:for="{{availableShapes}}"
wx:key="id"
class="shape-option {{tempSelectedShapeIds.indexOf(item.id) !== -1 ? 'selected' : ''}} {{selectedShapes.length >= 6 && tempSelectedShapeIds.indexOf(item.id) === -1 ? 'disabled' : ''}}"
bindtap="toggleShape"
data-shape-id="{{item.id}}">
<!-- 选中标识 -->
<view
class="selected-mark"
wx:if="{{tempSelectedShapeIds.indexOf(item.id) !== -1}}">
<van-icon name="success" color="white" size="12" />
</view>
<!-- 形状预览 -->
<view class="shape-preview-box">
<image
src="{{item.svgContent}}"
class="shape-svg-small"
mode="aspectFit">
</image>
</view>
<text class="shape-name">{{item.name}}</text>
</view>
</view>
<view class="popup-actions">
<button
class="confirm-btn"
bindtap="confirmShapeSelection"
type="primary">
确认选择
</button>
</view>
</view>
</van-popup>
<!-- 颜色选择器 -->
<color-picker
show="{{showColorPicker}}"
currentColor="{{currentEditingColor}}"
bind:onClose="closeColorPicker"
bind:onChange="onColorChange">
</color-picker>
<!-- Canvas 画布 (隐藏) -->
<canvas type="2d" id="mainCanvas" class="hidden-canvas"></canvas>
</view>
-134
View File
@@ -1,134 +0,0 @@
// shapes.js - 幼儿园到小学三年级基本几何形状定义
const shapes = [
{
id: 'circle',
name: '圆形',
type: 'circle',
svg: '<circle cx="50" cy="50" r="40" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawCircle',
},
{
id: 'square',
name: '正方形',
type: 'rect',
svg: '<rect x="10" y="10" width="80" height="80" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawSquare',
},
{
id: 'rectangle',
name: '长方形',
type: 'rect',
svg: '<rect x="5" y="25" width="90" height="50" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawRectangle',
},
{
id: 'triangle',
name: '三角形',
type: 'triangle',
svg: '<polygon points="50,10 20,80 80,80" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawTriangle',
},
{
id: 'oval',
name: '椭圆形',
type: 'oval',
svg: '<ellipse cx="50" cy="50" rx="40" ry="25" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawOval',
},
{
id: 'diamond',
name: '菱形',
type: 'diamond',
svg: '<polygon points="50,10 80,50 50,90 20,50" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawDiamond',
},
{
id: 'star',
name: '星形',
type: 'star',
svg: '<polygon points="50,5 60,35 90,35 70,55 80,85 50,70 20,85 30,55 10,35 40,35" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawStar',
},
{
id: 'heart',
name: '心形',
type: 'heart',
svg: '<path d="M50,85 C30,65 10,45 10,25 C10,15 20,5 30,5 C40,5 50,15 50,25 C50,15 60,5 70,5 C80,5 90,15 90,25 C90,45 70,65 50,85 Z" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawHeart',
},
{
id: 'hexagon',
name: '六边形',
type: 'hexagon',
svg: '<polygon points="25,15 75,15 90,50 75,85 25,85 10,50" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawHexagon',
},
{
id: 'pentagon',
name: '五边形',
type: 'pentagon',
svg: '<polygon points="50,10 80,35 65,75 35,75 20,35" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawPentagon',
},
{
id: 'semicircle',
name: '半圆形',
type: 'semicircle',
svg: '<path d="M 15 50 A 35 35 0 0 1 85 50 Z" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawSemicircle',
},
{
id: 'trapezoid',
name: '梯形',
type: 'trapezoid',
svg: '<polygon points="30,20 70,20 85,80 15,80" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawTrapezoid',
},
// 新增3D形状
{
id: 'cylinder',
name: '圆柱体',
type: '3d-cylinder',
svg: '<ellipse cx="50" cy="20" rx="30" ry="8" stroke="#333" stroke-width="2" fill="none"/><rect x="20" y="20" width="60" height="50" stroke="none" fill="none"/><line x1="20" y1="20" x2="20" y2="70" stroke="#333" stroke-width="2"/><line x1="80" y1="20" x2="80" y2="70" stroke="#333" stroke-width="2"/><ellipse cx="50" cy="70" rx="30" ry="8" stroke="#333" stroke-width="2" fill="none"/>',
drawFunction: 'drawCylinder',
},
{
id: 'cube',
name: '正方体',
type: '3d-cube',
svg: '<polygon points="20,30 60,30 60,70 20,70" stroke="#333" stroke-width="2" fill="none"/><polygon points="30,20 70,20 70,60 30,60" stroke="#333" stroke-width="2" fill="none"/><line x1="20" y1="30" x2="30" y2="20" stroke="#333" stroke-width="2"/><line x1="60" y1="30" x2="70" y2="20" stroke="#333" stroke-width="2"/><line x1="60" y1="70" x2="70" y2="60" stroke="#333" stroke-width="2"/>',
drawFunction: 'drawCube',
},
{
id: 'sphere',
name: '球体',
type: '3d-sphere',
svg: '<circle cx="50" cy="50" r="35" stroke="#333" stroke-width="2" fill="none"/><ellipse cx="50" cy="50" rx="35" ry="15" stroke="#333" stroke-width="1" fill="none" opacity="0.5"/><ellipse cx="50" cy="50" rx="15" ry="35" stroke="#333" stroke-width="1" fill="none" opacity="0.5"/>',
drawFunction: 'drawSphere',
},
{
id: 'cone',
name: '圆锥体',
type: '3d-cone',
svg: '<ellipse cx="50" cy="80" rx="30" ry="8" stroke="#333" stroke-width="2" fill="none"/><line x1="20" y1="80" x2="50" y2="20" stroke="#333" stroke-width="2"/><line x1="80" y1="80" x2="50" y2="20" stroke="#333" stroke-width="2"/>',
drawFunction: 'drawCone',
},
];
// 颜色选项
const colors = [
{ name: '红色', value: '#FF4444' },
{ name: '蓝色', value: '#4A90E2' },
{ name: '绿色', value: '#7ED321' },
{ name: '黄色', value: '#F5A623' },
{ name: '紫色', value: '#9013FE' },
{ name: '橙色', value: '#FF8F00' },
{ name: '粉色', value: '#E91E63' },
{ name: '青色', value: '#00BCD4' },
{ name: '深绿', value: '#4CAF50' },
{ name: '深蓝', value: '#2196F3' },
{ name: '棕色', value: '#8D6E63' },
{ name: '灰色', value: '#9E9E9E' },
];
export { shapes, colors };
@@ -1,54 +0,0 @@
Page({
data: {
showModal: false,
texts: ['', '', '', ''],
},
onReady: function () {
this.drawCircles();
},
drawCircles: function () {
const ctx = wx.createCanvasContext('myCanvas');
const { texts } = this.data;
// 绘制4个圆圈
for (let i = 0; i < 4; i++) {
const x = 50 + i * 100;
const y = 50;
ctx.beginPath();
ctx.arc(x, y, 40, 0, 2 * Math.PI);
ctx.setFillStyle('white');
ctx.fill();
ctx.setStrokeStyle('black');
ctx.stroke();
if (texts[i]) {
ctx.setFillStyle('black');
ctx.setFontSize(20);
ctx.setTextAlign('center');
ctx.setTextBaseline('middle');
ctx.fillText(texts[i], x, y);
}
}
ctx.draw();
},
showInput: function () {
this.setData({ showModal: true });
},
updateText: function (e) {
const index = e.currentTarget.dataset.index;
const value = e.detail.value;
const { texts } = this.data;
texts[index] = value;
this.setData({ texts });
},
confirmInput: function () {
this.setData({ showModal: false });
this.drawCircles();
},
});
@@ -1,7 +0,0 @@
{
"navigationBarTitleText": "文本绘制 示例",
"navigationBarBackgroundColor": "#d2e7d8",
"homeButton": true,
"backgroundColor": "#e8eddb",
"enablePullDownRefresh": false
}
@@ -1,13 +0,0 @@
.page-container {
height: 100vh;
background-color: transparent;
overflow-y: hidden;
.text-canvas {
margin: 20rpx 20rpx 60rpx;
width: calc(100% - 40rpx);
height: calc(100% - 80rpx);
border: 1rpx solid #333;
background-color: #fff;
}
}
@@ -1,94 +0,0 @@
Page({
canvas: null as WechatMiniprogram.Canvas | null,
ctx: null as CanvasRenderingContext2D | null,
data: {
showModal: false,
texts: ['', '', '', ''],
},
onReady: function () {
this.initCanvas();
},
initCanvas() {
const query = wx.createSelectorQuery();
query
.select('#textCanvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
this.canvas = canvas;
this.ctx = ctx;
const dpi = 300; // 明确声明 DPI
const mmToInch = 25.4;
const width = (210 / mmToInch) * dpi; // 2480px
const height = (297 / mmToInch) * dpi; // 3508px
const pixelRatio = wx.getWindowInfo().pixelRatio;
// 设置画布实际像素为 A4 尺寸
canvas.width = width;
canvas.height = height;
// 适配屏幕显示(非必须,仅用于预览)
canvas.style = canvas.style || {};
canvas.style.width = `${width / pixelRatio}px`;
canvas.style.height = `${height / pixelRatio}px`;
// 缩放上下文以适配高清屏
ctx.scale(pixelRatio, pixelRatio);
this.drawCircles();
});
},
setCanvasSize() { },
drawCircles: function () {
if (!this.canvas) {
return;
}
const ctx = this.ctx as CanvasRenderingContext2D;
const { texts } = this.data;
// 清空画布
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 绘制4个圆圈
for (let i = 0; i < 4; i++) {
const x = 50 + i * 100;
const y = 50;
ctx.beginPath();
ctx.arc(x, y, 40, 0, 2 * Math.PI);
ctx.fillStyle = 'white';
ctx.fill();
ctx.strokeStyle = 'black';
ctx.stroke();
if (texts[i]) {
ctx.fillStyle = 'black';
ctx.font = '20px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(texts[i], x, y);
}
}
},
showInput: function () {
this.setData({ showModal: true });
},
updateText: function (e: WechatMiniprogram.CustomEvent) {
const index = e.currentTarget.dataset.index;
const value = e.detail.value;
const { texts } = this.data;
texts[index] = value;
this.setData({ texts });
},
confirmInput: function () {
this.setData({ showModal: false });
this.drawCircles();
},
});
@@ -1,13 +0,0 @@
<view class="container">
<canvas canvas-id="myCanvas" style="width: 100%; height: 100vh;"></canvas>
<button class="input-button" bindtap="showInput">输入文本</button>
<view wx:if="{{showModal}}" class="modal">
<view class="modal-content">
<input placeholder="输入第一个文本" bindinput="updateText" data-index="0" />
<input placeholder="输入第二个文本" bindinput="updateText" data-index="1" />
<input placeholder="输入第三个文本" bindinput="updateText" data-index="2" />
<input placeholder="输入第四个文本" bindinput="updateText" data-index="3" />
<button bindtap="confirmInput">确认</button>
</view>
</view>
</view>
@@ -1,34 +0,0 @@
.container {
position: relative;
}
canvas {
width: 100%;
height: 100vh;
}
.input-button {
position: absolute;
bottom: 20px;
right: 20px;
z-index: 10;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 10px;
width: 80%;
}
@@ -1,7 +0,0 @@
{
"navigationBarTitleText": "文本绘制 示例",
"navigationBarBackgroundColor": "#798154",
"homeButton": false,
"backgroundColorContent": "#ffffff00",
"enablePullDownRefresh": false
}
@@ -1,41 +0,0 @@
// .color-picker {
// display: flex;
// justify-content: center;
// padding: 20rpx;
// }
// .color-circle {
// width: 80rpx;
// height: 80rpx;
// border-radius: 50%;
// margin: 0 40rpx;
// border: 4rpx solid #333;
// }
// .action-btn {
// width: 60%;
// background: #4CAF50;
// color: white;
// margin: 40rpx auto;
// }
.container {
height: 100vh;
background-color: transparent;
background-color: transparent;
display: flex;
align-items: center;
flex-direction: column;
}
.canvas-container {
margin-top: 20rpx;
// margin: 20rpx;
// width: calc(100% - 40rpx);
// // width: 100%;
// height: 580px;
// height: calc(100% - 80rpx);
border: 2rpx solid #333;
background-color: #fff;
box-sizing: border-box;
}
@@ -1,283 +0,0 @@
// import TextDrawService from '../../canvasService/textDrawService';
// import TextDrawServiceV2 from '../../canvasService/textDrawServiceV2';
import { PAPER_SIZE } from '../../canvasService/constant';
import TextDrawServiceV3 from '../../canvasService/textDrawServiceV3';
Page({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
data: {
characters: ['鱼', '鸟', '牛', '羊'] as const, // 固定汉字类型
colorMap: {
: '#2196F3', // 蓝色
: '#FFEB3B', // 黄色
: '#FF9800', // 橙色
: '#9C27B0', // 紫色
} as Record<string, string>,
matrix: [] as string[], // 随机汉字矩阵
selectedColor: '', // 当前选中颜色
selectedChar: '', // 当前选中汉字
},
onLoad() {
console.log('textPaint2 onLoad');
// this.setCanvasBoxSize();
// this.generateMatrix();
},
onReady() {
wx.nextTick(() => {
this.setCanvasBoxSize();
});
},
// setCanvasBoxSize() {
// const { width: paperWidth, height: paperHeight } = PAPER_SIZE['A4'];
// // 逻辑尺寸(容器宽高)
// const boxWidth = windowWidth - 40;
// const boxHeight = boxWidth / (paperWidth / paperHeight);
// console.log('boxWidth---:', boxWidth);
// console.log('boxHeight---:', boxHeight);
// this.setData({ boxWidth, boxHeight });
// // // 实际像素尺寸(需乘以设备像素比)
// // const canvasWidth = boxWidth * pixelRatio;
// // const canvasHeight = boxHeight * pixelRatio;
// // // 更新 Canvas 节点属性
// // const query = wx.createSelectorQuery().select('#textCanvas');
// // query.fields({ node: true, size: true }).exec((res) => {
// // const canvas = res[0].node;
// // canvas.width = canvasWidth;
// // canvas.height = canvasHeight;
// // // 缩放上下文以适配逻辑尺寸
// // const ctx = canvas.getContext('2d');
// // ctx.scale(pixelRatio, pixelRatio);
// // this.canvas = canvas;
// // this.ctx = ctx;
// // const textDrawService = new TextDrawServiceV3(canvas, ctx);
// // textDrawService.draw();
// // ctx.fillStyle = '#fff'; // 设置背景色为白色
// // ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// // });
// },
// 生成6列随机矩阵
generateMatrix() {
const shuffled = [...this.data.characters]
.sort(() => Math.random() - 0.5)
.concat(
...Array(42)
.fill(null)
.map(() => this.data.characters[Math.floor(Math.random() * 4)]),
);
this.setData({ matrix: shuffled.slice(0, 42) }); // 6列x7行
},
setCanvasBoxSize() {
const { width, height } = PAPER_SIZE['A4'];
const { windowWidth } = wx.getWindowInfo();
const boxWidth = windowWidth - 20;
const boxHeight = boxWidth / (width / height);
console.log('boxWidth----:', boxWidth);
console.log('boxHeight----:', boxHeight);
this.setData({ boxHeight, boxWidth }, () => {
this.initCanvas();
});
},
// 初始化A4画布(300DPI
initCanvas() {
wx.createSelectorQuery()
.select('#textCanvas')
.fields({
node: true,
size: true,
})
.exec((res) => {
if (res[0] && res[0].node) {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
if (ctx) {
this.canvas = canvas;
this.ctx = ctx;
const rect = res[0];
console.log('rect-----:', rect);
// const width = rect.width * dpr;
// const height = rect.height * dpr;
// 设置canvas的实际尺寸
// canvas.width = width;
// canvas.height = height;
// ctx.scale(dpr, dpr);
// 使用TextDrawServiceV3绘制
const textDrawService = new TextDrawServiceV3(canvas, ctx);
textDrawService.draw();
}
}
// const canvas = res[0].node;
// const ctx = canvas.getContext('2d');
// this.canvas = canvas;
// this.ctx = ctx;
// const textDrawService = new TextDrawServiceV3(canvas, ctx);
// textDrawService.draw();
// this.setCanvasSize(canvas, ctx);
// ctx.fillRect(0, 0, 2480, 3508);
// this.drawGameBoard(ctx);
});
},
setCanvasSize(canvas: Canvas, ctx: RenderingContext) {
const A4 = {
width: 2480,
height: 3508,
};
const { pixelRatio: dpr, windowWidth, windowHeight } = wx.getWindowInfo();
// const printWidth = A4.width;
// const printHeight = A4.height;
// @ts-ignore
const originWidth = canvas._width || windowWidth;
// @ts-ignore
const originHeight = canvas._height || windowHeight;
console.log('originWidth---:', originWidth);
console.log('originHeight---:', originHeight);
canvas.width = A4.width;
canvas.height = A4.height;
console.log('canvas----:', canvas);
// 计算显示比例
// const scaleRatio = Math.min(originWidth / printWidth, originHeight / printHeight);
// console.log('scaleRatio---:', scaleRatio);
// console.log('dpr---:', dpr);
// 设置坐标系
// ctx.scale(scaleRatio * dpr, scaleRatio * dpr);
},
// 绘制完整游戏界面
drawGameBoard(ctx?: RenderingContext) {
const canvas = this.canvas as Canvas;
ctx = ctx || (this.ctx as RenderingContext);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff'; // 设置填充色为白色
ctx.fillRect(0, 0, 2480, 3508); // 覆盖整个A4画布
// 绘制标题
// 绘制标题并居中显示
ctx.font = 'bold 80px "Microsoft Yahei"';
ctx.fillStyle = '#333';
ctx.textAlign = 'center';
const titleX = canvas.width / 2;
const titleY = 150;
ctx.fillText('找一找 涂 色', titleX, titleY);
// 绘制颜色示例(顶部)
this.drawColorLegend(ctx, 250);
// 绘制汉字矩阵(主体)
this.drawCharacterMatrix(ctx, 600);
},
// 绘制颜色示例
drawColorLegend(ctx: RenderingContext, y: number) {
console.log('this.data:', this.data);
let x = 500;
this.data.characters.forEach((char) => {
ctx.beginPath();
ctx.arc(x, y, 50, 0, Math.PI * 2);
ctx.fillStyle = this.data.colorMap[char] as string;
ctx.fill();
ctx.strokeStyle = '#333';
ctx.lineWidth = 4;
ctx.stroke();
x += 300;
});
},
// 绘制汉字矩阵(6列x7行)
drawCharacterMatrix(ctx: RenderingContext, startY: number) {
const cols = 6;
const cellSize = 350; // 每个单元格大小
const colorMap = this.data.colorMap;
this.data.matrix.forEach((char: string, index) => {
char = char || '';
const row = Math.floor(index / cols);
const col = index % cols;
// 计算坐标
const x = 300 + col * cellSize;
const y = startY + row * cellSize;
// 绘制圆形
ctx.beginPath();
ctx.arc(x, y, 120, 0, Math.PI * 2);
ctx.fillStyle = '#fff';
ctx.fill();
ctx.strokeStyle = '#333';
ctx.lineWidth = 4;
ctx.stroke();
// 绘制汉字
ctx.font = 'bold 100px "Microsoft Yahei"';
ctx.fillStyle = (colorMap[char] as string) || '';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(char, x, y);
});
},
// 点击颜色选择
handleColorSelect(e: WechatMiniprogram.TouchEvent) {
const index = e.currentTarget.dataset.index;
const selectedChar = this.data.characters[index];
this.setData({
selectedColor: this.data.colorMap[selectedChar],
selectedChar,
});
},
// 点击汉字涂色
handleCharacterTap(e: WechatMiniprogram.TouchEvent) {
const index = e.currentTarget.dataset.index;
if (!this.data.selectedColor) return;
const matrix = this.data.matrix.map((char, i) =>
i === index ? this.data.selectedChar : char,
);
this.setData({ matrix }, () => {
this.drawGameBoard();
});
},
// 导出A4图片
exportToPrint() {
console.log('this.canvas:', this.canvas);
if (this.canvas) {
wx.canvasToTempFilePath({
canvas: this.canvas,
fileType: 'png',
quality: 1,
success: (res) => {
// wx.getImageInfo({
// src: res.tempFilePath,
// success: (res) =>
// console.log('getImageInfo image size :', res.width, res.height), // 应输出2480x3508
// });
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => wx.showToast({ title: '保存成功' }),
});
},
});
return;
}
},
});
@@ -1,17 +0,0 @@
<view class="container">
<!-- 颜色选择区 -->
<!-- <view class="color-picker">
<block wx:for="{{characters}}" wx:key="index">
<view
class="color-circle"
style="background: {{colorMap.get(item)}}"
bindtap="handleColorSelect"
data-index="{{index}}"></view>
</block>
</view> -->
<!-- 画布容器 -->
<canvas type="2d" id="textCanvas" class="canvas-container" style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
<view>当前宽度:{{boxWidth}}px,高度:{{boxHeight}}px</view>
<!-- 操作按钮 -->
<button class="action-btn" bindtap="exportToPrint">导出A4图片</button>
</view>
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1,11 @@
{
"navigationBarTitleText": "看数字,涂一涂",
"navigationBarBackgroundColor": "#FFD719",
"homeButton": true,
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup"
}
}
@@ -0,0 +1,168 @@
page {
background-color: #f6f6f6;
}
.page-container {
background-color: #f6f6f6;
padding: 0 24rpx;
box-sizing: border-box;
padding-bottom: 160rpx; // 为底部按钮预留空间
.empty {
height: 50rpx;
}
}
.wrapper {
background-color: #ffffff;
border-radius: 20rpx;
padding: 36rpx 24rpx;
box-shadow: 0 6rpx 8rpx rgba(0, 0, 0, 0.15);
margin: 30rpx 0;
display: flex;
flex-direction: column;
.wrapper-title {
font-size: 32rpx;
color: #141414;
margin-bottom: 36rpx;
font-weight: bold;
text-align: center;
}
.canvas-wrapper {
display: flex;
justify-content: center;
align-items: center;
min-height: 400rpx;
background: #f8f9fa;
border-radius: 12rpx;
border: 2rpx dashed #dee2e6;
}
.canvas-content {
max-width: 100%;
border-radius: 8rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
}
// .canvas-wrapper {
// width: 100%;
// margin-bottom: 40rpx;
// display: flex;
// justify-content: center;
// align-items: center;
// background: #f8f8f8;
// border-radius: 12rpx;
// padding: 20rpx;
// box-sizing: border-box;
// }
// .canvas-content {
// width: 100%;
// height: 100%;
// }
}
/* 数字选择区域 */
.number-selection-area {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.number-row {
display: flex;
flex-direction: row;
gap: 60rpx;
width: 100%;
margin-top: 40rpx;
}
.number-item {
background: #ffffff;
border: 2rpx solid #e8e8e8;
border-radius: 16rpx;
padding: 24rpx;
cursor: pointer;
transition: all 0.2s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
flex: 1;
box-sizing: border-box;
min-width: 0;
position: relative;
&:active {
transform: scale(0.98);
}
&.active {
border-color: #93d333;
box-shadow: 0 0 0 4rpx rgba(147, 211, 51, 0.2);
}
.number-text {
font-size: 28rpx;
color: #141414;
font-weight: bold;
text-align: center;
}
/* 随机按钮样式 */
&.random-button {
flex: 2;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
.random-icon {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.toy-icon {
font-size: 32rpx;
color: #141414;
}
}
.number-text {
// flex: 0;
}
&.active {
border-color: #93d333;
box-shadow: 0 0 0 4rpx rgba(147, 211, 51, 0.2);
.random-icon .toy-icon {
color: #93d333;
}
}
}
}
/* 底部按钮区域 */
.bottom-btn-box {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 200rpx;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
box-shadow: 0px -3.9rpx 5.2rpx rgba(0, 0, 0, 0.15);
padding: 0 24rpx;
padding-bottom: env(safe-area-inset-bottom);
box-sizing: border-box;
border-radius: 16rpx 16rpx 0 0;
z-index: 100;
}
@@ -0,0 +1,211 @@
import { PAPER_SIZE } from '../../constants/colors';
import { checkAndSaveImage } from '../../utils/saveImage';
import { shouldShowShareGuide } from '../../utils/shareGuide';
Page({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
data: {
pageTitle: '看数字,涂一涂', // 默认标题
selectedNumber: 0, // 选中的数字
functionId: '', // 功能ID,用于判断标题
numberList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], // 数字列表
showShareDialog: false, // 显示分享引导弹窗
},
onLoad(options: { id?: string }) {
// 根据id参数判断页面标题
const functionId = options.id || 'number-find-color';
this.setData({
functionId,
});
// 根据functionId设置页面标题
const titleMap: Record<string, string> = {
'number-find-color': '看数字,涂一涂',
'number-coloring': '数字涂色卡',
'counting-matching': '数一数,连一连',
'missing-number': '填上缺少的数字',
compare: '数一数,比大小',
'counting-select': '数一数,选一选',
'counting-fill': '数一数,填一填',
'addition-5': '5以内加法',
'addition-10': '10以内加法',
'addition-subtraction-10': '10以内加减法',
};
const pageTitle = titleMap[functionId] || '看数字,涂一涂';
this.setData({
pageTitle,
});
// 设置导航栏标题
wx.setNavigationBarTitle({
title: pageTitle,
});
},
onReady() {
// 初始化canvas
this.initCanvas();
},
/**
* 初始化Canvas
*/
initCanvas() {
const query = wx.createSelectorQuery();
query
.select('#canvasWrapper')
.boundingClientRect((rect) => {
if (rect) {
const { width, height } = PAPER_SIZE['A4'];
const boxWidth = rect.width;
const boxHeight = boxWidth / (width / height);
this.boxHeight = boxHeight;
this.boxWidth = boxWidth;
this.setData({
boxWidth,
boxHeight,
});
// 初始化canvas上下文
const canvas = wx
.createSelectorQuery()
.select('#canvasContent');
canvas.fields({ node: true, size: true }).exec((res) => {
if (res[0]) {
const canvasNode = res[0].node;
const ctx = canvasNode.getContext('2d');
const dpr = wx.getSystemInfoSync().pixelRatio;
canvasNode.width = boxWidth * dpr;
canvasNode.height = boxHeight * dpr;
ctx.scale(dpr, dpr);
this.canvas = canvasNode;
this.ctx = ctx;
// 绘制初始内容
this.drawCanvas();
}
});
}
})
.exec();
},
/**
* 绘制Canvas内容
*/
drawCanvas() {
if (!this.ctx) {
return;
}
const ctx = this.ctx;
const width = this.boxWidth;
const height = this.boxHeight;
// 清空画布
ctx.clearRect(0, 0, width, height);
// 设置背景色
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, width, height);
// TODO: 根据选中的数字绘制内容
if (this.data.selectedNumber > 0) {
// 绘制示例:显示选中的数字
ctx.fillStyle = '#141414';
ctx.font = 'bold 120px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(
String(this.data.selectedNumber),
width / 2,
height / 2,
);
}
},
/**
* 选择数字
*/
onSelectNumber(e: WechatMiniprogram.TouchEvent) {
const number = parseInt(e.currentTarget.dataset.number);
this.setData({
selectedNumber: number,
});
this.drawCanvas();
},
/**
* 随机选择数字
*/
onRandom() {
const randomNumber = Math.floor(Math.random() * 10) + 1;
this.setData({
selectedNumber: randomNumber,
});
this.drawCanvas();
},
/**
* 导出打印
*/
exportToPrint() {
if (!this.canvas || this.data.selectedNumber <= 0) {
return;
}
// 检查是否需要显示分享引导
if (shouldShowShareGuide()) {
this.setData({ showShareDialog: true });
return;
}
// 直接下载
checkAndSaveImage(this.canvas);
},
/**
* 分享小程序
*/
onShareAppMessage() {
return {
title: '涂鸦丫-数学学习涂鸦卡',
path: `/mathPages/numberColor/numberColor?id=${this.data.functionId}`,
imageUrl:
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
};
},
onShareTimeline() {
return {
title: '涂鸦丫-数学学习涂鸦卡',
query: `id=${this.data.functionId}`,
imageUrl:
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
};
},
/** 关闭分享引导弹窗 */
onCloseShareDialog() {
this.setData({ showShareDialog: false });
},
/** 分享成功回调 */
onShareSuccess() {
this.setData({ showShareDialog: false });
// 分享成功后直接下载
if (this.canvas) {
checkAndSaveImage(this.canvas);
}
},
});
@@ -0,0 +1,94 @@
<view class="page-container">
<view class="wrapper">
<text class="wrapper-title">预览打印效果</text>
<!-- 预览打印效果 -->
<view id="canvasWrapper" class="canvas-wrapper">
<canvas
type="2d"
id="canvasContent"
class="canvas-content"
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
</view>
<!-- 数字选择区域 -->
<view class="number-selection-area">
<!-- 第一行:1-4 -->
<view class="number-row">
<view
wx:for="{{numberList}}"
wx:key="index"
wx:for-item="number"
wx:if="{{index < 4}}"
class="number-item {{selectedNumber === number ? 'active' : ''}}"
data-number="{{number}}"
bind:tap="onSelectNumber">
<view class="number-text">{{number}}</view>
</view>
</view>
<!-- 第二行:5-8 -->
<view class="number-row">
<view
wx:for="{{numberList}}"
wx:key="index"
wx:for-item="number"
wx:if="{{index >= 4 && index < 8}}"
class="number-item {{selectedNumber === number ? 'active' : ''}}"
data-number="{{number}}"
bind:tap="onSelectNumber">
<view class="number-text">{{number}}</view>
</view>
</view>
<!-- 第三行:9、10和随机按钮 -->
<view class="number-row">
<view
wx:for="{{numberList}}"
wx:key="index"
wx:for-item="number"
wx:if="{{index >= 8}}"
class="number-item {{selectedNumber === number ? 'active' : ''}}"
data-number="{{number}}"
bind:tap="onSelectNumber">
<view class="number-text">{{number}}</view>
</view>
<view class="number-item random-button" bind:tap="onRandom">
<view class="random-icon">
<text class="toy-icon toy-icon-refresh"></text>
</view>
<view class="number-text">随机</view>
</view>
</view>
</view>
</view>
<view class="empty"></view>
</view>
<view class="bottom-btn-box">
<toy-button
openType="share"
type="green"
flat="{{true}}"
bind:click="onShareAppMessage"
width="220rpx"
height="80rpx"
icon="wechat"
icon-class-prefix="toy-icon">
分享
</toy-button>
<toy-button
type="primary"
flat="{{true}}"
bind:click="exportToPrint"
width="420rpx"
height="80rpx"
disabled="{{selectedNumber <= 0}}"
icon="download"
icon-class-prefix="toy-icon">
下载打印
</toy-button>
</view>
<share-guide-popup
show="{{showShareDialog}}"
bind:onClose="onCloseShareDialog"
bind:onShareSuccess="onShareSuccess" />
@@ -1,10 +0,0 @@
{
"navigationBarTitleText": "按钮组件测试",
"navigationBarBackgroundColor": "#FFD719",
"backgroundColor": "#F6F6F6",
"usingComponents": {
"toy-button": "../../ui/button/button",
"van-icon": "@vant/weapp/icon/index",
"word-card": "../../components/word-card/word-card"
}
}
@@ -1,34 +0,0 @@
page {
background-color: #F6F6F6;
}
.button-test-container {
padding: 40rpx;
}
.test-section {
margin-bottom: 60rpx;
}
.section-title {
display: block;
font-size: 32rpx;
font-weight: 600;
color: #141414;
margin-bottom: 24rpx;
}
.button-group {
display: flex;
flex-direction: column;
gap: 24rpx;
align-items: flex-start;
}
.word-cards-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32rpx;
width: 100%;
max-width: 600rpx;
}
@@ -1,28 +0,0 @@
Page({
data: {},
onButtonClick(e: WechatMiniprogram.TouchEvent) {
console.log('按钮点击:', e);
wx.showToast({
title: '按钮被点击了',
icon: 'success',
duration: 1000
});
},
onWordColorTap(e: any) {
console.log('点击了颜色:', e);
wx.showToast({
title: '点击了颜色',
icon: 'none',
});
},
onWordDelete(e: any) {
console.log('删除文字:', e);
wx.showToast({
title: '删除文字',
icon: 'none',
});
}
});
@@ -1,160 +0,0 @@
<view class="button-test-container">
<view class="test-section">
<text class="section-title">主要按钮 (Primary)</text>
<view class="button-group">
<toy-button type="primary" bind:click="onButtonClick"
>主要按钮</toy-button
>
<toy-button type="primary" disabled bind:click="onButtonClick"
>禁用状态</toy-button
>
<toy-button type="primary" loading bind:click="onButtonClick"
>加载中</toy-button
>
</view>
</view>
<view class="test-section">
<text class="section-title">次要按钮 (Secondary)</text>
<view class="button-group">
<toy-button type="secondary" bind:click="onButtonClick"
>次要按钮</toy-button
>
<toy-button type="secondary" disabled bind:click="onButtonClick"
>禁用状态</toy-button
>
<toy-button type="secondary" loading bind:click="onButtonClick"
>加载中</toy-button
>
</view>
</view>
<view class="test-section">
<text class="section-title">绿色按钮 (Green)</text>
<view class="button-group">
<toy-button type="green" bind:click="onButtonClick"
>绿色按钮</toy-button
>
<toy-button type="green" disabled bind:click="onButtonClick"
>禁用状态</toy-button
>
<toy-button type="green" loading bind:click="onButtonClick"
>加载中</toy-button
>
</view>
</view>
<view class="test-section">
<text class="section-title">白色按钮 (White)</text>
<view class="button-group">
<toy-button type="white" bind:click="onButtonClick"
>白色按钮</toy-button
>
<toy-button type="white" disabled bind:click="onButtonClick"
>禁用状态</toy-button
>
<toy-button type="white" plain bind:click="onButtonClick"
>透明模式</toy-button
>
</view>
</view>
<view class="test-section">
<text class="section-title">扁平化按钮 (Flat)</text>
<view class="button-group">
<toy-button type="primary" flat bind:click="onButtonClick"
>扁平主要</toy-button
>
<toy-button type="secondary" flat bind:click="onButtonClick"
>扁平次要</toy-button
>
<toy-button type="white" flat bind:click="onButtonClick"
>扁平白色</toy-button
>
</view>
</view>
<view class="test-section">
<text class="section-title">带图标按钮</text>
<view class="button-group">
<toy-button type="primary" icon="add" bind:click="onButtonClick"
>添加</toy-button
>
<toy-button
type="secondary"
icon="delete"
bind:click="onButtonClick"
>删除</toy-button
>
<toy-button type="white" icon="edit" bind:click="onButtonClick"
>编辑</toy-button
>
</view>
</view>
<view class="test-section">
<text class="section-title">不同尺寸</text>
<view class="button-group">
<toy-button
type="primary"
width="200rpx"
bind:click="onButtonClick">
小按钮
</toy-button>
<toy-button
type="secondary"
width="300rpx"
bind:click="onButtonClick"
>中按钮</toy-button
>
<toy-button type="white" width="400rpx" bind:click="onButtonClick">
大按钮
</toy-button>
</view>
</view>
</view>
<view class="test-section">
<text class="section-title">点击动效测试</text>
<view class="button-group">
<toy-button type="primary" bind:click="onButtonClick"
>点击我测试动效</toy-button
>
<toy-button type="secondary" bind:click="onButtonClick"
>抖动效果</toy-button
>
<toy-button type="white" bind:click="onButtonClick"
>缩放动画</toy-button
>
</view>
</view>
<view class="test-section">
<text class="section-title">Word Card 测试</text>
<view class="word-cards-grid">
<word-card
word="王"
color="#FFD719"
bind:colorTap="onWordColorTap"
bind:delete="onWordDelete">
</word-card>
<word-card
word="手"
color="#93D333"
bind:colorTap="onWordColorTap"
bind:delete="onWordDelete">
</word-card>
<word-card
word="哭"
color="#FF6B9D"
bind:colorTap="onWordColorTap"
bind:delete="onWordDelete">
</word-card>
<word-card
word="少"
color="#4ECDC4"
bind:colorTap="onWordColorTap"
bind:delete="onWordDelete">
</word-card>
</view>
</view>
@@ -0,0 +1,8 @@
{
"navigationBarTitleText": "涂鸦丫-数学",
"navigationBarBackgroundColor": "#FFD719",
"homeButton": true,
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {}
}
+165
View File
@@ -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;
}
+98
View File
@@ -0,0 +1,98 @@
Page({
data: {
functionList: [
{
id: 'number-find-color',
page: 'numberColor',
title: '看数字,涂一涂',
desc: '给一个数字,找一找下面相同的数字,涂上颜色',
icon: '🔍',
},
{
id: 'number-coloring',
title: '数字涂色卡',
desc: '一边写数字、一边学数数,数字和卡通图片一一对应',
icon: '🎨',
},
{
id: 'counting-matching',
title: '数一数,连一连',
desc: '通过连线配对数字和对应的数量图形',
icon: '🔗',
},
{
id: 'missing-number',
title: '填上缺少的数字',
desc: '在数字序列中找出并填写缺失的数字',
icon: '❓',
},
{
id: 'compare',
title: '数一数,比大小',
desc: '通过数数比较两组物品的数量大小',
icon: '⚖️',
},
{
id: 'counting-select',
title: '数一数,选一选',
desc: '数出物品数量,从多个选项中选择正确答案',
icon: '✓',
},
{
id: 'counting-fill',
title: '数一数,填一填',
desc: '数出物品数量,填写对应的数字',
icon: '✏️',
},
{
id: 'addition-5',
title: '5以内加法',
desc: '练习5以内的加法运算,图形化展示',
icon: '',
},
{
id: 'addition-10',
title: '10以内加法',
desc: '练习10以内的加法运算,图形化展示',
icon: '',
},
{
id: 'addition-subtraction-10',
title: '10以内加减法',
desc: '练习10以内的加法和减法混合运算',
icon: '±',
},
],
},
onLoad() {
// 页面加载
},
/**
* 打开数学功能页面
*/
openMathFunction(e: WechatMiniprogram.TouchEvent) {
const functionId = e.currentTarget.dataset.function;
const functionItem = this.data.functionList.find(
(item) => item.id === functionId,
);
if (!functionItem) {
return;
}
// 如果有page字段,跳转到对应页面
if (functionItem.page) {
wx.navigateTo({
url: `/mathPages/${functionItem.page}/${functionItem.page}?id=${functionId}`,
});
} else {
wx.showToast({
title: `即将打开:${functionItem.title}`,
icon: 'none',
duration: 2000,
});
}
},
});
@@ -0,0 +1,42 @@
<view class="page-container">
<!-- 页面标题和描述 -->
<view class="math-page-header">
<view class="math-page-title">数学学习涂鸦卡</view>
<view class="math-page-subtitle"
>选择功能,生成可打印的数学学习涂鸦卡</view
>
</view>
<!-- 功能列表 -->
<view class="math-functions-list">
<view
class="math-function-item"
wx:for="{{functionList}}"
wx:key="id"
data-function="{{item.id}}"
bind:tap="openMathFunction">
<view class="function-item-image">
<view class="function-image-placeholder">{{item.icon}}</view>
</view>
<view class="function-item-content">
<view class="function-item-title">{{item.title}}</view>
<view class="function-item-desc">{{item.desc}}</view>
</view>
<view class="function-item-arrow">→</view>
</view>
</view>
<!-- 使用提示 -->
<view class="usage-hint">
<view class="hint-icon">💡</view>
<view class="hint-text">
<view class="hint-title">使用提示</view>
<view class="hint-desc">
1. 点击上方功能卡片选择要生成的涂鸦卡类型\n 2.
选择数字范围、图形类型等参数\n 3. 预览生成的涂鸦卡效果\n 4.
下载A4格式PNG图片,打印后使用
</view>
</view>
</view>
<view class="empty"></view>
</view>
+16 -2
View File
@@ -23,12 +23,26 @@
"condition": { "condition": {
"miniprogram": { "miniprogram": {
"list": [ "list": [
{
"name": "mathPages/numberColor/numberColor",
"pathName": "mathPages/numberColor/numberColor",
"query": "id=number-find-color",
"scene": null,
"launchMode": "default"
},
{
"name": "pages/mathIndex/mathIndex",
"pathName": "pages/mathIndex/mathIndex",
"query": "",
"launchMode": "default",
"scene": null
},
{ {
"name": "pages/copyBook/copyBook", "name": "pages/copyBook/copyBook",
"pathName": "pages/copyBook/copyBook", "pathName": "pages/copyBook/copyBook",
"query": "", "query": "",
"scene": null, "launchMode": "default",
"launchMode": "default" "scene": null
}, },
{ {
"name": "pages/buttonTest/buttonTest", "name": "pages/buttonTest/buttonTest",