头图

创建移动应用,配置证书等步骤,在此不表
具体见wx.miniapp.shareMiniProgramMessage中说明

一、分享配置写法

  1. 小程序写法

    Page({
     onShareAppMessage() {
         const promise = new Promise(resolve => {
             setTimeout(() => {
                 resolve({
                     title: '自定义转发标题'
                 })
             }, 2000)
         })
         return {
             title: '自定义转发标题',
             path: '/page/user?id=123',
             promise 
         }
     }
    })
  2. donut写法

    Page({
     onShareAppMessage() {
         const promise = new Promise(resolve => {
             setTimeout(() => {
                 resolve({
                     userName: '小程序原始id',    
                     path: 'pages/index/index',
                     title: '标题',
                     imagePath: '/pages/thumb.png',
                     webpageUrl: 'www.qq.com',
                     withShareTicket: true,
                     miniprogramType: 0,
                     scene: 0, 
                 })
             }, 2000)
         })
         return {
             userName: '小程序原始id',    
             path: 'pages/index/index',
             title: '标题',
             imagePath: '/pages/thumb.png',
             webpageUrl: 'www.qq.com',
             withShareTicket: true,
             miniprogramType: 0,
             scene: 0, 
             promise 
         }
     }
    })

    很明显可以发现,donut只有2点不一样

    1. 少了一些userNamewebpageUrl等配置
    2. 有一些配置项的转换,如imageUrl转换为imagePath

上述两点只需要在某个全局的地方加上即可

统一兼容方式

注意在这需要使用donut的条件编译(或者在require处使用条件编译也可)
否则微信小程序中也会有这段不需要的代码

  1. 重写PageComponent,使用handleAppShareInfo拦截onShareAppMessage方法

    const originalPage = Page;
    Page = function (e) {
     const app = getApp();
     if (e.onShareAppMessage) {
         e.onShareAppMessage = handleAppShareInfo(e.onShareAppMessage);
     }
     return originalPage(e);
    };
    
    const originalComponent = Component;
    Component = function (e) {
     if (e.methods && e.methods.onShareAppMessage) {
         e.methods.onShareAppMessage = handleAppShareInfo(e.methods.onShareAppMessage);
     }
     return originalComponent(e);
    };
  2. 添加小程序端缺失的配置

    function appShareInfoAdapter(result) {
     return Object.assign({}, {
         userName: '小程序原始id',
         description: '',
         imagePath: res.imageUrl || '',
         webpageUrl: 'https://www.qq.com',
         withShareTicket: false,
         miniprogramType: 0,
         scene: 0,
     }, result, { imageUrl: void 0, })
    }
    function handleAppShareInfo(originalShare) {
     return function (...args) {
         const originResult = originalShare.apply(this, args)
         const result = appShareInfoAdapter(originResult)
         if (result.promise) {
             result.promise = result.promise.then(appShareInfoAdapter)
         }
         return result
     };
    }

    二、图片大小限制

  • 之后运行发现,有时候会报错图片超过128KB限制(具体见微信文档安卓/IOS),得先将大于128kb的图片压缩
    128kblimit
  • 但是对png文件进行wx.compressImage无效
    image.png
  • 这是一个微信官方已确认的bug,无法压缩png文件

    此处提到的把thumbData的转换逻辑改下,对应到我们开发者,
    是需要project.miniapp.json文件中的sdkVersion改成1.3.7-beta.2-656(或者大于此版本)
    image.png

所以在图片大于128kb的情况下,得把分享图片转换成jpg再进行压缩,以下是具体代码

  1. 代码模块内canvas变量&128kb限制常量

    /** @type { WechatMiniprogram.FileSystemManager } */
    let globalFileSystemManager
    const size128kb = 131072
    function getFileSystemManager() {
      return globalFileSystemManager || (globalFileSystemManager = wx.getFileSystemManager())
    }
  2. 因为app分享只能使用本地路径,所以需要实现转换本地路径逻辑

    /**
     * 获取分享图本地路径
     * @param { string } imagePath 图片路径
     * @returns { Promise<string> }
     */
    function getShareImageLocalPath(imagePath) {
     return imagePath.startsWith('http') ? new Promise((resolve) => {
         wx.downloadFile({
             url: imagePath,
             success(res) {
                 resolve(res.tempFilePath)
             },
             fail() {
                 resolve('')
             }
         })
     }) : imagePath
    }
  3. png转换jpg逻辑

    /**
     * 将图片转换为jpg格式
     * @param { string } imagePath 非jpg图片路径
     * @param { WechatMiniprogram.GetImageInfoSuccessCallbackResult } imageInfo
     * @returns { Promise<string> }
     */
    async function function convert2JPG(imagePath, imageInfo) {
      const { width, height } = imageInfo
      const canvas = wx.createOffscreenCanvas({ type: '2d', width, height })
      const context = canvas.getContext('2d')
      const image = canvas.createImage()
      await new Promise(resolve => {
     image.onload = resolve
     image.src = imagePath
      })
      context.clearRect(0, 0, width, height)
      context.drawImage(image, 0, 0, width, height)
      /** @type { Promise<string> } */
      return new Promise(resolve => {
     // #if ANDROID
     wx.canvasToTempFilePath({
       width,
       height,
       destWidth: width,
       destHeight: height,
       canvas,
       fileType: 'jpg',
       quality: 1,
       success(res) {
         resolve(res.tempFilePath)
       },
       fail() { resolve('') }
     })
     // #elif IOS
     const imagePath = `${wx.env.USER_DATA_PATH}/temp-${Date.now()}.jpg`
     getFileSystemManager().writeFile({
       filePath: imagePath,
       data: canvas.toDataURL('image/jpeg').replace(/^data:image\/\w+;base64,/, ''),
       encoding: 'base64',
       success() {
         resolve(imagePath)
       },
       fail() {
         resolve('')
       },
     })
     // #endif
      })
    }
  4. 压缩图片

    /**
     * 压缩分享图片
     * @param { string } imageLocalPath 本地图片路径
     * @param { boolean } skipPngCheck 跳过图片格式检查
     * @returns { Promise<string> }
     */
    async function compressImageShareImage(imageLocalPath, skipPngCheck = false) {
     /** @type { number } */
     const imageSize = await new Promise(resolve => {
         getFileSystemManager().getFileInfo({
             filePath: imageLocalPath,
             success(res) {
                 resolve(res.size)
             },
             fail() {
                 resolve(0)
             }
         })
     })
     if (imageSize <= size128kb) {
         return imageLocalPath
     }
     if (!skipPngCheck) {
         /** @type { WechatMiniprogram.GetImageInfoSuccessCallbackResult }    */
         const imageInfo = await new Promise(resolve => wx.getImageInfo({
             src: imageLocalPath,
             success: resolve,
             fail() {
                 resolve({ width: 0, height: 0 })
             }
         }))
         // wx.compressImage无法压缩png图片
         // 官方开发人员已经确认的bug,并且暂时无法修改
         // 所以,先转换成jpg再进行压缩
         if (imageInfo.type === 'png') {
             const jpgImage = await convert2JPG(imageLocalPath, imageInfo)
             // 转换之后,再走一次compressImageShareImage里面的imageSize逻辑
             // 如果jpgImage比128kb还小,就不需要再压缩了
             return compressImageShareImage(jpgImage, true)
         }
     }
     return new Promise(resolve => wx.compressImage({
         src: imageLocalPath,
         quality: Math.max(1, Math.floor(100 * size128kb / imageSize)),
         success(res) {
             resolve(res.tempFilePath)
         },
         fail() {
             resolve('')
         }
     }))
    }
  5. 修改onShareAppMessage返回值

    function handleAppShareInfo(originalShare) {
     return function (...args) {
         const originResult = originalShare.apply(this, args)
         const result = appShareInfoAdapter(originResult)
         result.promise = (
             result.promise
                 ? result.promise.then(appShareInfoAdapter)
                 : Promise.resolve(result)
         ).then(async res => {
             /** @type { { imagePath: string } } */
             const { imagePath } = res
             /** @type { string } */
             const imageLocalPath = await getShareImageLocalPath(imagePath)
             res.imagePath = await compressImageShareImage(imageLocalPath)
             return res
         })
         return result
     };
    }

643104191
2.4k 声望993 粉丝