Files
doodle-mini/miniprogram/demoPages/shapePrint/index.js
T
2025-07-22 17:16:57 +08:00

1014 lines
30 KiB
JavaScript

// 引入形状和颜色数据
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('形状绘制页面加载完成');
// 初始化临时选择列表
this.setData({
tempSelectedShapeIds: this.data.selectedShapes.map(
(shape) => shape.id,
),
});
},
// 打开形状选择器
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) {
// 保持原有颜色
newSelectedShapes.push(existingShape);
const colorIndex = this.data.basic12Colors.findIndex(
(c) => c.hex === existingShape.fillColor,
);
if (colorIndex !== -1) {
usedColorIndexes.push(colorIndex);
}
} else {
// 新形状,分配随机颜色
const randomColor =
this.getRandomUnusedColor(usedColorIndexes);
newSelectedShapes.push({
id: Date.now() + index,
shapeId: shape.id,
name: shape.name,
fillColor: randomColor.hex,
colorName: randomColor.name,
drawFunction: shape.drawFunction,
});
}
}
});
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 colorInfo = basic12Colors.find((c) => c.hex === color) ||
WATER_COLORS.extended24.find((c) => c.hex === color) || {
name: '自定义色',
hex: color,
};
newSelectedShapes[currentEditingIndex] = {
...newSelectedShapes[currentEditingIndex],
fillColor: color,
colorName: colorInfo.name,
};
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);
// 导出图片
const tempFilePath = canvas.toTempFilePathSync({
fileType: 'png',
quality: 1,
});
this.setData({
generatedImage: tempFilePath,
isGenerating: false,
});
wx.showToast({
title: '生成完成',
icon: 'success',
});
} 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,
});
}
},
});