uniapp 微信小程序自定义分享图片

avatar
作者
猴君
阅读量:5

场景:微信小程序用户,点击小程序里商品的分享按钮时,想要不同的商品展示不用的分享内容,比如分享图片上展示商品的图片、价格等信息。分享的UI图如下: 

 

实现方法:

1. 分享按钮:<button open-type="share">onShareAppMessage(OBJECT)

2. 自定义内容:因为onShareAppMessage的imageUrl参数的支持的是本地文件路径、代码包文件路径或者网络图片路径 ,所以这里实现自定义的分享的方法是结合canvas画布uni.createCanvasContext(canvasId, this),将画好的内容利用uni.canvasToTempFilePath(object, component)导出生成指定大小的图片,再将返回的文件路径赋值给imageUrl,即可实现。

 具体可看官网:分享 | uni-app官网 、uni.createCanvasContext(canvasId, this) | uni-app官网uni-app官网uni.canvasToTempFilePath(object, component) | uni-app官网

代码:先贴上画布的代码,这里画了三种自定义分享的内容,样式在代码下方。

1. 新建一个组件文件:ShareCanvas.vue

<template>   <view class="ShareCanvas">     <view class="canvas">       <canvas canvas-id="shareCanvas" />     </view>   </view> </template>  <script> export default {   name: 'ShareCanvas',   methods: {     // 订单分享     setOrderCanvas(info) {       return new Promise(async (resolve, reject) => {         console.log('订单分享-info', info);         try {           const ctx = uni.createCanvasContext('shareCanvas', this)           // 绘制背景图           ctx.setFillStyle('#19C161')           ctx.fillRect(0, 0, 211, 170) // 保证宽高比是 5:4            // 绘制文本信息           ctx.setFontSize(21);           ctx.setTextAlign('left')           ctx.setFillStyle('#FFFFFF')           ctx.fillText('我买好啦!', 9, 32)            // 浅绿色背景           this.setRadius(ctx, 10, 106, 11, 97, 27) // 加圆角           ctx.setFillStyle('#EFF9F1')           ctx.fill()           // ctx.fillRect(106, 11, 97, 27) // x, y, width, height            ctx.setFontSize(14);           ctx.setTextAlign('center')           ctx.setFillStyle('#19C161')           ctx.fillText('跟团号:' + info.followNum, 155, 30)            this.setRadius(ctx, 3, 9, 49, 194, 106)           ctx.setFillStyle('#FFFFFF')           ctx.fill()           // ctx.fillRect(9, 49, 194, 106) // 不设置圆角的时候这么画有背景色的矩形            // 画商品图           ctx.save();           this.setRadius(ctx, 5, 17, 59, 85, 85)           ctx.clip();//画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内           try {             const { path } = await this.getImge(info.orderCartInfos[0].productImg)             ctx.drawImage(path, 17, 59, 85, 85)           } catch (error) {             console.error(error);           }           ctx.restore();            if (info.teamLeaderUser && info.teamLeaderUser.avatar) {             // 团长头像             ctx.save();             this.setRadius(ctx, 5, 110, 60, 25, 25)             ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内             try {               const { path } = await this.getImge(info.teamLeaderUser.avatar)               ctx.drawImage(path, 110, 60, 25, 25)             } catch (error) {               console.error(error);             }             ctx.restore();           }            if (info.teamLeaderUser && info.teamLeaderUser.nickname) {             // 团长昵称             ctx.setFontSize(12);             ctx.setTextAlign('left')             ctx.setFillStyle('#96999B')             ctx.fillText(info.teamLeaderUser.nickname.length > 4 ? info.teamLeaderUser.nickname.slice(0, 4) + '...' : info.teamLeaderUser.nickname, 140, 76)           }            ctx.setFontSize(14);           ctx.setTextAlign('center')           ctx.setFillStyle('#FB7415')           ctx.fillText(`¥${info.orderCartInfos[0].unitPrice}`, 152, 105)            this.setRadius(ctx, 10, 115, 118, 75, 26)            const grd = ctx.createLinearGradient(115, 118, 115, 144)           grd.addColorStop(0, '#FDAC2F')           grd.addColorStop(0.5, '#FDA72C')           grd.addColorStop(1, '#FB5615')            // 橙色按钮背景           ctx.setFillStyle(grd)           // ctx.fillRect(230/2, 218/2, 149/2, 53/2) // x, y, width, height           ctx.fill()            ctx.setFontSize(12);           ctx.setTextAlign('center')           ctx.setFillStyle('#FFFFFF')           ctx.fillText('去看看 >', 152, 135)            ctx.draw(false, (() => {             setTimeout(() => {               uni.canvasToTempFilePath({                 canvasId: 'shareCanvas',                 success: (res) => {                   return resolve(res.tempFilePath)                 },                 fail: function (error) {                   console.log('fail----fail', error);                   //TODO                   return reject(error)                 }               }, this)             }, 500);           }))         } catch (error) {           console.log('画图失败error', error);           return reject(error)         }       })     },     // 商品分享     setGoodsShareCanvas(info) {       console.log('商品分享--info', info);       return new Promise(async (resolve, reject) => {         try {           const ctx = uni.createCanvasContext('shareCanvas', this)           // 绘制背景图           ctx.setFillStyle('#FFFFFF')           ctx.fillRect(0, 0, 211, 170)            // 团长头像           ctx.save();           this.setRadius(ctx, 5, 0, 0, 30, 30)           ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内           try {             const { path } = await this.getImge(info.avatar)             ctx.drawImage(path, 0, 0, 30, 30)           } catch (error) {             console.error(error);           }           ctx.restore();            // 团长昵称           ctx.setFontSize(12);           ctx.setTextAlign('left')           ctx.setFillStyle('#96999B')           ctx.fillText(info.nickname.length > 11 ? info.nickname.slice(0, 11) + `${info.pinkId ? '...的团' : '...'}` : info.nickname + '的团', 35, 18)            // 商品1图           ctx.save();           this.setRadius(ctx, 3, 0, 35, 211, 211)           ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内           try {             const { path } = await this.getImge(info.image)             ctx.drawImage(path, 0, 35, 211, 211)           } catch (error) {             console.error(error);           }           ctx.restore();            // 绿色背景           ctx.setFillStyle('#19C161')           ctx.fillRect(0, 130, 131, 40) // x, y, width, height            ctx.setFontSize(16);           ctx.setTextAlign('center')           ctx.setFillStyle('#FFFFFF')           ctx.fillText(`¥${info.pinkId ? info.pinkPrice : info.price}起`, 65, 130 + 18)            ctx.setFontSize(12);           ctx.setTextAlign('center')           ctx.setFillStyle('#FFFFFF')           ctx.fillText(`¥${info.otPrice}`, 65, 130 + 34)            // 划线           ctx.beginPath()           ctx.setLineWidth(1);           ctx.setStrokeStyle('#FFFFFF')           ctx.moveTo(40, 130 + 30)           ctx.lineTo(90, 130 + 30)           ctx.stroke()            // 深绿色背景           ctx.setFillStyle('#19AF5C')           ctx.fillRect(131, 130, 211 - 131, 40)            if (info.pinkId) {             // 立即跟团按钮             ctx.setFontSize(16);             ctx.setTextAlign('center')             ctx.setFillStyle('#FFFFFF')             ctx.fillText(`立即跟团`, 131 + (211 - 131) / 2, 130 + 26)           } else {             // 已团数量             ctx.setFontSize(12);             ctx.setTextAlign('center')             ctx.setFillStyle('#FFFFFF')             ctx.fillText(`已团 ${this.getSales(info.sales)} 件`, 131 + (211 - 131) / 2, 130 + 25)           }            ctx.draw(false, (() => {             uni.canvasToTempFilePath({               canvasId: 'shareCanvas',               success: (res) => {                 return resolve(res.tempFilePath)               },               fail: function (error) {                 console.log('fail----fail', error);                 //TODO                 return reject(error)               }             }, this)           }))         } catch (error) {           uni.hideLoading()           console.log('画图失败error', error);           return reject(error)         }       })     },     // 团分享     setGroupShareCanvas(info) {       console.log('团分享-info', info);       return new Promise(async (resolve, reject) => {         try {           const ctx = uni.createCanvasContext('shareCanvas', this)           // 绘制背景图           ctx.setFillStyle('#FFFFFF')           ctx.fillRect(0, 0, 211, 170)            if (info.productImageList.length == 1) {             // 团长头像             ctx.save();             this.setRadius(ctx, 5, 0, 0, 30, 30)             ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内             try {               const { path } = await this.getImge(info.avatar)               ctx.drawImage(path, 0, 0, 30, 30)             } catch (error) {               console.error(error);             }             ctx.restore();              // 团长昵称             ctx.setFontSize(12);             ctx.setTextAlign('left')             ctx.setFillStyle('#96999B')             ctx.fillText((info.nickname.length > 11 ? info.nickname.slice(0, 11) + '...' : info.nickname) + '的团', 35, 18)              // 商品1图             ctx.save();             this.setRadius(ctx, 3, 0, 35, 211, 211)             ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内             try {               const { path } = await this.getImge(info.productImageList[0])               ctx.drawImage(path, 0, 35, 211, 211)             } catch (error) {               console.error(error);             }             ctx.restore();           }            if (info.productImageList.length >= 2) {             // 团长头像             ctx.save();             this.setRadius(ctx, 5, 0, 0, 42, 42)             ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内             try {               const { path } = await this.getImge(info.avatar)               ctx.drawImage(path, 0, 0, 42, 42)             } catch (error) {               console.error(error);             }             ctx.restore();              // 团长昵称             ctx.setFontSize(12);             ctx.setTextAlign('left')             ctx.setFillStyle('#96999B')             ctx.fillText((info.nickname.length > 10 ? info.nickname.slice(0, 10) + '...' : info.nickname) + '的团', 47, 25)              // 商品1图             ctx.save();             this.setRadius(ctx, 3, 0, 51, 69, 69)             ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内             try {               const { path } = await this.getImge(info.productImageList[0])               ctx.drawImage(path, 0, 51, 69, 69)             } catch (error) {               console.error(error);             }             ctx.restore();              // 商品2图             ctx.save();             this.setRadius(ctx, 3, 69 + 2, 51, 69, 69)             ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内             try {               const { path } = await this.getImge(info.productImageList[1])               ctx.drawImage(path, 69 + 2, 51, 69, 69)             } catch (error) {               console.error(error);             }             ctx.restore();              if (info.productImageList.length >= 3) {               // 商品3图               ctx.save();               this.setRadius(ctx, 3, 69 * 2 + 4, 51, 69, 69)               ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内               try {                 const { path } = await this.getImge(info.productImageList[2])                 ctx.drawImage(path, 69 * 2 + 4, 51, 69, 69)               } catch (error) {                 console.error(error);               }               ctx.restore();             }           }            // 绿色背景           ctx.setFillStyle('#19C161')           ctx.fillRect(0, 128, 211, 42) // x, y, width, height            ctx.setFontSize(16);           ctx.setTextAlign('center')           ctx.setFillStyle('#FFFFFF')           ctx.fillText(`立即跟团`, 211 / 2, 128 + 26)            ctx.draw(false, (() => {             uni.canvasToTempFilePath({               canvasId: 'shareCanvas',               success: (res) => {                 return resolve(res.tempFilePath)               },               fail: function (error) {                 console.log('fail----fail', error);                 //TODO                 return reject(error)               }             }, this)           }))         } catch (error) {           console.log('画图失败error', error);           return reject(error)         }       })     },     /**      * 设置圆角矩形      *      * @param ctx 绘图上下文      * @param cornerRadius 圆角半径      * @param width 矩形宽度      * @param height 矩形高度      * @param x 矩形左上角的 x 坐标      * @param y 矩形左上角的 y 坐标      * @returns 无返回值      */     setRadius(ctx, cornerRadius, x, y, width, height) {       // 开始绘制路径       ctx.beginPath();       // 绘制最左侧的圆角       ctx.arc(x + cornerRadius, y + cornerRadius, cornerRadius, Math.PI, Math.PI * 1.5);       // 绘制顶部边缘       ctx.moveTo(x + cornerRadius, y);       ctx.lineTo(x + width - cornerRadius, y);       ctx.lineTo(x + width, y + cornerRadius);       // 绘制最右侧的圆角       ctx.arc(x + width - cornerRadius, y + cornerRadius, cornerRadius, Math.PI * 1.5, Math.PI * 2);       // 绘制右侧边缘       ctx.lineTo(x + width, y + height - cornerRadius);       ctx.lineTo(x + width - cornerRadius, y + height);       // 绘制最下侧的圆角       ctx.arc(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0, Math.PI * 0.5);       // 绘制底部边缘       ctx.lineTo(x + cornerRadius, y + height);       ctx.lineTo(x, y + height - cornerRadius);       // 绘制最左侧的圆角       ctx.arc(x + cornerRadius, y + height - cornerRadius, cornerRadius, Math.PI * 0.5, Math.PI);       // 绘制左侧边缘       ctx.lineTo(x, y + cornerRadius);       ctx.lineTo(x + cornerRadius, y);       // 闭合路径       ctx.closePath();     },     // 获取图片地址     getImge(path) {       // 利用promise异步转同步,否则可能显示不了~       return new Promise((resolve, reject) => {         uni.getImageInfo({           src: path,           success: function (res) {             if (res && res.path) {               resolve(res)             } else {               reject(false)             }           },           fail: function (res) {             reject(res)           }         })       })     },     getSales(sales) {       return sales >= 10000 ? sales / 10000 + 'w+' : sales     },   } } </script>  <style lang="scss" scoped> // 隐藏画布 .ShareCanvas {   position: absolute;   top: -200px;   z-index: -1;   opacity: 0;   .canvas canvas {     width: 211px;     height: 170px; // +16   } } </style>

2. 在分享按钮的页面使用这个画布组件。

onShareAppMessage 方法的内容:

注意:

  1. onShareAppMessage 方法要和 onLoad 等生命周期函数同级
  2. 因为里面画布生成图片是异步的,我在上面用Promise处理了,这里需要async await接收~
async onShareAppMessage(res) {     const { id, title, avatar, nickname, productDetailList } = this.detailInfo     if (res.target && res.target.id) { // 分享商品       console.log('分享商品');       const item = productDetailList.find(p => p.id == res.target.id) || {}       try {         uni.showLoading({ title: '分享信息生成中', mask: true })         const imageUrl = await this.$refs.ShareCanvas.setGoodsShareCanvas({ ...item, avatar, nickname, pinkId: id }) // 用不同的画布画样式,就调对应的方法名,注意里面需要的参数要传对         uni.hideLoading()         return {           title: item.storeName || '好物多多,快来选购啦~',           path: '/pages/home/index', // 这里是你的分享里面的跳转地址           imageUrl: imageUrl || ''         }       } catch (error) {         uni.hideLoading()       }     } else {       // 分享团       console.log('分享团', productDetailList);       try {         uni.showLoading({ title: '分享信息生成中', mask: true })         const productImageList = productDetailList.map(item => item.image)         const imageUrl = await this.$refs.ShareCanvas.setGroupShareCanvas({ avatar, nickname, productImageList }) // 用不同的画布画样式,就调对应的方法名,注意里面需要的参数要传对         uni.hideLoading()         return {           title: title || '好物多多,快来选购啦~',           path: '/pages/home/index', // 这里是你的分享里面的跳转地址           imageUrl: imageUrl || ''         }       } catch (error) {         uni.hideLoading()       }     }   },

setOrderCanvas()方法的样式 

 

setGoodsShareCanvas()方法的样式

setGroupShareCanvas()方法的样式

画画的时候,要是找不准xy的位置,可以从这三种样式里选一个样式接近的再慢慢修改~

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!