import { PAPER_SIZE } from '../../constants/colors'; import { checkAndSaveImage } from '../../utils/saveImage'; import { shouldShowShareGuide } from '../../utils/shareGuide'; import { BaseMathDrawService } from '../service/baseMathDraw'; import { MATH_FUNCTION_TYPES, MathFunctionType, } from '../../constants/mathFunctions'; import tracker from '../../utils/tracker'; /** * Canvas 相关的页面实例属性 */ export interface MathPageCanvasInstance { canvas: Canvas | null; ctx: RenderingContext | null; boxHeight: number; boxWidth: number; drawService: BaseMathDrawService | null; setData(data: any, callback?: () => void): void; } /** * Canvas 数据状态接口 */ export interface CanvasDataState { hasContent: boolean; showShareDialog: boolean; boxWidth: number; boxHeight: number; functionId: string; pageTitle: string; subTitle?: string; } /** * Canvas 初始化选项 */ export interface InitCanvasOptions { /** * 创建绘制服务的工厂函数 */ createDrawService: ( canvas: Canvas, ctx: RenderingContext, options?: Record, ) => BaseMathDrawService; /** * 绘制服务的选项 */ drawServiceOptions?: Record; /** * Canvas 初始化完成后回调 */ onCanvasReady?: () => void; } /** * 分享配置选项 */ export interface ShareOptions { /** * 页面路径(相对于 mathPages 目录,例如:'missingNumber/missingNumber') */ pagePath: string; } const shareConfig = { title: '涂鸦丫-数学学习涂鸦卡', imageUrl: 'https://cdn.joeyone.cn/doodle/share-img/index-share-v2.png', }; /** * 获取数学页面的公共方法 * 这些方法可以在所有数学页面中复用 */ export function getMathPageCommonMethods() { return { /** * 初始化 Canvas */ initCanvas( this: MathPageCanvasInstance & { data: CanvasDataState }, options: InitCanvasOptions, ) { console.log('initCanvas this', this); const query = wx.createSelectorQuery(); query .select('#canvasWrapper') .boundingClientRect((rect) => { if (!rect) return; 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 }); 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; // 创建绘制服务 const drawServiceOptions = { title: this.data.pageTitle, subTitle: this.data.subTitle || '', ...options.drawServiceOptions, }; this.drawService = options.createDrawService( canvasNode, ctx, drawServiceOptions, ); // 执行初始化完成回调 if (options.onCanvasReady) { options.onCanvasReady.call(this); } } }); }) .exec(); }, /** * 导出打印 */ exportToPrint( this: MathPageCanvasInstance & { data: CanvasDataState }, ) { if (!this.canvas || !this.data.hasContent) { return; } if (shouldShowShareGuide()) { this.setData({ showShareDialog: true }); return; } // 上报下载埋点 tracker.reportDownload(this.data.pageTitle); checkAndSaveImage(this.canvas); }, /** * 分享小程序 */ onShareAppMessage( this: MathPageCanvasInstance & { data: CanvasDataState }, ) { console.log('onShareAppMessage'); // 上报分享埋点 tracker.reportShare(this.data.pageTitle); return { ...shareConfig, query: `id=${this.data.functionId}`, }; }, /** * 分享到朋友圈 */ onShareTimeline( this: MathPageCanvasInstance & { data: CanvasDataState }, ) { console.log('onShareTimeline'); // 上报分享埋点 tracker.reportShare(this.data.pageTitle); return { ...shareConfig, query: `id=${this.data.functionId}`, }; }, /** * 关闭分享引导弹窗 */ onCloseShareDialog( this: MathPageCanvasInstance & { data: CanvasDataState }, ) { this.setData({ showShareDialog: false }); }, /** * 分享成功回调 */ onShareSuccess( this: MathPageCanvasInstance & { data: CanvasDataState }, ) { this.setData({ showShareDialog: false }); if (this.canvas) { // 上报下载埋点(分享成功后下载) tracker.reportDownload(this.data.pageTitle); checkAndSaveImage(this.canvas); } }, /** * 初始化页面信息(从 functionId 获取标题等信息) */ initPageInfo( this: MathPageCanvasInstance & { data: CanvasDataState }, functionId: string, defaultTitle?: string, ) { const functionItem = MATH_FUNCTION_TYPES.find( (item: MathFunctionType) => item.id === functionId, ); const title = functionItem?.title || defaultTitle || ''; const desc = functionItem?.desc || ''; this.setData({ pageTitle: title, subTitle: desc, functionId, }); wx.setNavigationBarTitle({ title }); }, }; } /** * 应用数学页面公共方法的辅助函数 * 自动混入公共方法,简化页面代码 * @param pageOptions 页面选项 * @param shareOptions 分享配置选项 * @returns 合并后的页面选项 */ export function applyMathPageMixin(pageOptions: any) { const commonMethods = getMathPageCommonMethods(); // 提取 pageOptions 中的 data(如果有) const pageData = pageOptions.data || {}; // 合并页面选项和公共方法 // 注意:pageOptions 放在后面,这样页面可以覆盖公共方法 const mergedOptions: any = { ...commonMethods, ...pageOptions, // 处理 data 的合并(需要特殊处理,避免覆盖) data: { ...pageData, }, }; return mergedOptions; } /** * 创建数学页面的便捷函数 * 自动应用公共方法并注册为页面 * 页面路径从函数调用栈自动推断(从调用文件路径提取) * @param pageOptions 页面选项 */ export function createMathPage(pageOptions: any) { Page(applyMathPageMixin(pageOptions)); }