Files
doodle-mini/miniprogram/pages/shape/index.ts
T
2025-08-08 17:32:39 +08:00

294 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { SHAPES, ShapeCard } from '../../constants/shapes';
import { WATER_COLORS, PAPER_SIZE } from '../../constants/colors';
import ShapeDrawService from '../../service/shapeDrawService';
import { checkAndSaveImage } from '../../utils/saveImage';
Page({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
shapeDrawService: null as ShapeDrawService | null,
data: {
shapeList: [] as ShapeCard[],
showSelectShapePopup: false,
currentShapeId: '',
showColorPopup: false,
currentIndex: 0,
currentColor: '',
isPCDevtool: false,
},
onLoad() {
// 判断是否在PC端开发工具上运行
// const systemInfo = wx.getDeviceInfo();
// if (systemInfo.platform === 'devtools') {
// this.setData({
// isPCDevtool: true,
// });
// }
this.refreshShapeCard();
},
onReady() {
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.initCanvas(boxWidth, boxHeight);
}
})
.exec();
},
initCanvas(boxWidth: number, boxHeight: number) {
wx.createSelectorQuery()
.select('#canvasContent')
.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];
this.shapeDrawService = new ShapeDrawService(canvas, ctx);
// shapeDrawService.draw();
this.setData({ boxWidth, boxHeight });
this.drawCanvas();
}
}
});
},
/** 变更shapeList 并更新canvas */
drawCanvas(
updatedShapeList?: ShapeCard[],
callback: () => void = () => { },
) {
if (updatedShapeList) {
this.setData({ shapeList: updatedShapeList }, () => {
this.shapeDrawService?.draw(updatedShapeList!);
callback();
});
} else {
const shapeList = this.data.shapeList;
this.shapeDrawService?.draw(shapeList);
}
},
openSelectShapePopup(e: any) {
const { key } = e.detail;
this.setData({
showSelectShapePopup: true,
currentShapeId: key
});
},
closeSelectShapePopup() {
this.setData({
showSelectShapePopup: false,
currentShapeId: ''
});
},
onChangeShape(e: any) {
const { shapes } = e.detail;
const prevShapeList: ShapeCard[] = this.data.shapeList || [];
// 1. 保留已选图形的 fillColor,不变
// 2. 新增的图形分配未被占用的颜色,且不重复
// 获取所有可用颜色
const colorList = WATER_COLORS.basic12.map(item => item.hex);
// 记录已被使用的颜色(只考虑当前 shapeList 中的 fillColor
const usedColors = prevShapeList.map(item => item.fillColor).filter(Boolean);
// 新的 shapeList
const newShapeList: ShapeCard[] = [];
// 记录已分配的颜色,避免重复
const assignedColors = [...usedColors];
// 先将 shapes 转为 Map,方便查找
const prevShapeMap = new Map(prevShapeList.map(item => [item.id, item]));
// 随机打乱剩余可用颜色
function shuffle(arr: string[]) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
// 计算未被占用的颜色
let availableColors = colorList.filter(color => !assignedColors.includes(color));
availableColors = shuffle(availableColors);
shapes.forEach((item: ShapeCard) => {
if (prevShapeMap.has(item.id)) {
// 已存在,保留原有 fillColor
newShapeList.push({
...item,
fillColor: prevShapeMap.get(item.id)!.fillColor
});
} else {
// 新增,分配未被占用的颜色
const color = availableColors.shift() || colorList[0];
assignedColors.push(color);
newShapeList.push({
...item,
fillColor: color
});
}
});
this.setData({
showSelectShapePopup: false,
currentShapeId: '',
shapeList: newShapeList
}, () => {
// 绘制canvas
this.drawCanvas();
});
},
/**
* 随机生成6个图形
*/
refreshShapeCard() {
// 随机取6个图形
const shapesCopy = [...SHAPES];
for (let i = shapesCopy.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shapesCopy[i], shapesCopy[j]] = [shapesCopy[j], shapesCopy[i]];
}
const selectedShapes = shapesCopy.slice(0, 6);
// 随机取6个颜色
const colorList = WATER_COLORS.basic12.map(item => item.hex);
const colorsCopy = [...colorList];
for (let i = colorsCopy.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[colorsCopy[i], colorsCopy[j]] = [colorsCopy[j], colorsCopy[i]];
}
const selectedColors = colorsCopy.slice(0, 6);
// 分配颜色到图形
const shapeList = selectedShapes.map((shape, idx) => ({
...shape,
fillColor: selectedColors[idx]
}));
// 更新数据
this.setData({
shapeList
}, () => {
// 绘制canvas
this.drawCanvas();
});
},
onDelete(e: any) {
const { index } = e.detail;
const newShapeList = this.data.shapeList.filter((item, i) => i !== index);
this.setData({
shapeList: newShapeList
}, () => {
// 绘制canvas
this.drawCanvas();
});
},
onColorTap(e: any) {
const { index, fillColor } = e.detail;
console.log('shape Page onColorTap', index, fillColor);
this.setData({
showColorPopup: true,
currentColor: fillColor,
currentIndex: index
});
},
onCloseColorPopup() {
this.setData({
showColorPopup: false,
currentColor: '',
currentIndex: 0
});
},
onChangeColor(e: any) {
const { color } = e.detail;
const { currentIndex } = this.data;
this.setData({
showColorPopup: false,
[`shapeList[${currentIndex}].fillColor`]: color
}, () => {
// 绘制canvas
this.drawCanvas();
})
},
// 测试ShapeDrawService
// testShapeDrawService() {
// if (!this.shapeDrawService) {
// wx.showToast({
// title: 'Canvas未初始化',
// icon: 'none'
// });
// return;
// }
// // 使用当前选中的图形进行测试
// const testShapes = this.data.shapeList.slice(0, 3); // 只测试前3个图形
// if (testShapes.length === 0) {
// wx.showToast({
// title: '请先选择图形',
// icon: 'none'
// });
// return;
// }
// this.shapeDrawService.draw(testShapes);
// wx.showToast({
// title: '绘制完成',
// icon: 'success'
// });
// },
onShareAppMessage() {
return {
title: '涂鸦丫-涂色|识字|画画|打印',
path: '/pages/index/index',
};
},
onShareTimeline() {
return {
title: '涂鸦丫-涂色|识字|画画|打印',
query: '/pages/index/index',
};
},
/** 下载打印 */
exportToPrint() {
if (this.canvas && this.data.shapeList.length > 0) {
checkAndSaveImage(this.canvas);
return;
}
},
});