import { SHAPES, ShapeCard } from '../../constants/shapes'; import { WATER_COLORS, PAPER_SIZE } from '../../constants/colors'; import ShapeDrawService from '../../service/shapeDrawService'; import { downloadPrint, tryUnlockWithRewardedAd } from '../../utils/downloadPrint'; import { shouldShowShareGuide, shouldUnlockWithAd, } from '../../utils/shareGuide'; 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(); }, ); }, /** 下载打印 */ async exportToPrint() { if (!this.canvas || this.data.shapeList.length <= 0) { return; } if (shouldUnlockWithAd()) { const unlocked = await tryUnlockWithRewardedAd(); if (!unlocked) { return; } } if (shouldShowShareGuide()) { this.setData({ showShareDialog: true }); return; } await downloadPrint(this.canvas, { errorToast: '请先生成图形', trackerName: '图形涂色', }); }, /** 关闭分享引导弹窗 */ 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', }; }, /** 分享成功回调(由组件触发) */ async onShareSuccess() { this.setData({ showShareDialog: false }); if (this.canvas && this.data.shapeList.length > 0) { await downloadPrint(this.canvas, { errorToast: '请先生成图形', trackerName: '图形涂色', }); } }, onUnload() { if (this.timer) { clearTimeout(this.timer); } }, });