Files
doodle-mini/miniprogram/focusPages/shape/shape.ts
T
2025-12-15 17:00:06 +08:00

347 lines
10 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';
import { shouldShowShareGuide } from '../../utils/shareGuide';
import tracker from '../../utils/tracker';
Page({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
shapeDrawService: null as ShapeDrawService | null,
timer: null as any,
data: {
shapeList: [] as ShapeCard[],
showSelectShapePopup: false,
selectedShapeIndex: -1,
showColorPopup: false,
currentIndex: 0,
currentColor: '',
isPCDevtool: false,
showShareDialog: 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);
}
},
handleSelectShape() {
this.setData({
showSelectShapePopup: true,
selectedShapeIndex: -1,
});
},
openSelectShapePopup(e: any) {
const { index } = e.detail;
this.setData({
showSelectShapePopup: true,
selectedShapeIndex: index,
});
},
closeSelectShapePopup() {
this.setData({
showSelectShapePopup: false,
});
},
onChangeShape(e: any) {
const { shapes, singleMode, selectedShapeIndex } = e.detail;
const prevShapeList: ShapeCard[] = this.data.shapeList || [];
let newShapeList: ShapeCard[] = [];
if (singleMode) {
// 单选模式,只替换 selectedShapeIndex 对应的 shape,其他 shape 保持不变和原有颜色
newShapeList = prevShapeList.map((item, idx) => {
if (idx === selectedShapeIndex) {
// 替换为新选中的 shape,保留原有 fillColor
return {
...shapes[0],
fillColor: item.fillColor,
};
} else {
// 其他 shape 不变
return item;
}
});
} else {
// 多选模式,原有逻辑
// 获取所有可用颜色
const colorList = WATER_COLORS.basic12.map((item) => item.hex);
// 记录已被使用的颜色(只考虑当前 shapeList 中的 fillColor
const usedColors = prevShapeList
.map((item) => item.fillColor)
.filter(Boolean);
// 记录已分配的颜色,避免重复
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,
shapeList: newShapeList,
},
() => {
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((_, i) => i !== index);
this.setData(
{
shapeList: newShapeList,
},
() => {
// 绘制canvas
this.drawCanvas();
},
);
},
onColorTap(e: any) {
const { index, fillColor } = e.detail;
this.setData({
showColorPopup: true,
currentColor: fillColor,
selectedShapeIndex: index,
});
},
onCloseColorPopup() {
this.setData({
showColorPopup: false,
currentColor: '',
selectedShapeIndex: 0,
});
},
onChangeColor(e: any) {
const { color } = e.detail;
const { selectedShapeIndex } = this.data;
this.setData(
{
showColorPopup: false,
[`shapeList[${selectedShapeIndex}].fillColor`]: color,
},
() => {
// 绘制canvas
this.drawCanvas();
},
);
},
/** 下载打印 */
exportToPrint() {
if (!this.canvas || this.data.shapeList.length <= 0) {
return;
}
// 检查是否需要显示分享引导
if (shouldShowShareGuide()) {
this.setData({ showShareDialog: true });
return;
}
// 上报下载埋点
tracker.reportDownload('图形涂色');
// 直接下载
checkAndSaveImage(this.canvas);
},
/** 关闭分享引导弹窗 */
onCloseShareDialog() {
this.setData({ showShareDialog: false });
},
onShareAppMessage() {
// 上报分享埋点
tracker.reportShare('图形涂色');
return {
title: '涂鸦丫-涂色|识字|图形|打印',
path: '/pages/shape/index',
imageUrl:
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
};
},
onShareTimeline() {
// 上报分享埋点
tracker.reportShare('图形涂色');
return {
title: '涂鸦丫-涂色|识字|图形|打印',
query: '/pages/shape/index',
imageUrl:
'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png',
};
},
/** 分享成功回调(由组件触发) */
onShareSuccess() {
// 只关闭弹窗,不触发下载
this.setData({ showShareDialog: false });
},
onUnload() {
if (this.timer) {
clearTimeout(this.timer);
}
},
});