当前位置:   article > 正文

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

微信小程序分享图片

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

  1. <template>
  2. <view class="ShareCanvas">
  3. <view class="canvas">
  4. <canvas canvas-id="shareCanvas" />
  5. </view>
  6. </view>
  7. </template>
  8. <script>
  9. export default {
  10. name: 'ShareCanvas',
  11. methods: {
  12. // 订单分享
  13. setOrderCanvas(info) {
  14. return new Promise(async (resolve, reject) => {
  15. console.log('订单分享-info', info);
  16. try {
  17. const ctx = uni.createCanvasContext('shareCanvas', this)
  18. // 绘制背景图
  19. ctx.setFillStyle('#19C161')
  20. ctx.fillRect(0, 0, 211, 170) // 保证宽高比是 5:4
  21. // 绘制文本信息
  22. ctx.setFontSize(21);
  23. ctx.setTextAlign('left')
  24. ctx.setFillStyle('#FFFFFF')
  25. ctx.fillText('我买好啦!', 9, 32)
  26. // 浅绿色背景
  27. this.setRadius(ctx, 10, 106, 11, 97, 27) // 加圆角
  28. ctx.setFillStyle('#EFF9F1')
  29. ctx.fill()
  30. // ctx.fillRect(106, 11, 97, 27) // x, y, width, height
  31. ctx.setFontSize(14);
  32. ctx.setTextAlign('center')
  33. ctx.setFillStyle('#19C161')
  34. ctx.fillText('跟团号:' + info.followNum, 155, 30)
  35. this.setRadius(ctx, 3, 9, 49, 194, 106)
  36. ctx.setFillStyle('#FFFFFF')
  37. ctx.fill()
  38. // ctx.fillRect(9, 49, 194, 106) // 不设置圆角的时候这么画有背景色的矩形
  39. // 画商品图
  40. ctx.save();
  41. this.setRadius(ctx, 5, 17, 59, 85, 85)
  42. ctx.clip();//画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  43. try {
  44. const { path } = await this.getImge(info.orderCartInfos[0].productImg)
  45. ctx.drawImage(path, 17, 59, 85, 85)
  46. } catch (error) {
  47. console.error(error);
  48. }
  49. ctx.restore();
  50. if (info.teamLeaderUser && info.teamLeaderUser.avatar) {
  51. // 团长头像
  52. ctx.save();
  53. this.setRadius(ctx, 5, 110, 60, 25, 25)
  54. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  55. try {
  56. const { path } = await this.getImge(info.teamLeaderUser.avatar)
  57. ctx.drawImage(path, 110, 60, 25, 25)
  58. } catch (error) {
  59. console.error(error);
  60. }
  61. ctx.restore();
  62. }
  63. if (info.teamLeaderUser && info.teamLeaderUser.nickname) {
  64. // 团长昵称
  65. ctx.setFontSize(12);
  66. ctx.setTextAlign('left')
  67. ctx.setFillStyle('#96999B')
  68. ctx.fillText(info.teamLeaderUser.nickname.length > 4 ? info.teamLeaderUser.nickname.slice(0, 4) + '...' : info.teamLeaderUser.nickname, 140, 76)
  69. }
  70. ctx.setFontSize(14);
  71. ctx.setTextAlign('center')
  72. ctx.setFillStyle('#FB7415')
  73. ctx.fillText(${info.orderCartInfos[0].unitPrice}`, 152, 105)
  74. this.setRadius(ctx, 10, 115, 118, 75, 26)
  75. const grd = ctx.createLinearGradient(115, 118, 115, 144)
  76. grd.addColorStop(0, '#FDAC2F')
  77. grd.addColorStop(0.5, '#FDA72C')
  78. grd.addColorStop(1, '#FB5615')
  79. // 橙色按钮背景
  80. ctx.setFillStyle(grd)
  81. // ctx.fillRect(230/2, 218/2, 149/2, 53/2) // x, y, width, height
  82. ctx.fill()
  83. ctx.setFontSize(12);
  84. ctx.setTextAlign('center')
  85. ctx.setFillStyle('#FFFFFF')
  86. ctx.fillText('去看看 >', 152, 135)
  87. ctx.draw(false, (() => {
  88. setTimeout(() => {
  89. uni.canvasToTempFilePath({
  90. canvasId: 'shareCanvas',
  91. success: (res) => {
  92. return resolve(res.tempFilePath)
  93. },
  94. fail: function (error) {
  95. console.log('fail----fail', error);
  96. //TODO
  97. return reject(error)
  98. }
  99. }, this)
  100. }, 500);
  101. }))
  102. } catch (error) {
  103. console.log('画图失败error', error);
  104. return reject(error)
  105. }
  106. })
  107. },
  108. // 商品分享
  109. setGoodsShareCanvas(info) {
  110. console.log('商品分享--info', info);
  111. return new Promise(async (resolve, reject) => {
  112. try {
  113. const ctx = uni.createCanvasContext('shareCanvas', this)
  114. // 绘制背景图
  115. ctx.setFillStyle('#FFFFFF')
  116. ctx.fillRect(0, 0, 211, 170)
  117. // 团长头像
  118. ctx.save();
  119. this.setRadius(ctx, 5, 0, 0, 30, 30)
  120. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  121. try {
  122. const { path } = await this.getImge(info.avatar)
  123. ctx.drawImage(path, 0, 0, 30, 30)
  124. } catch (error) {
  125. console.error(error);
  126. }
  127. ctx.restore();
  128. // 团长昵称
  129. ctx.setFontSize(12);
  130. ctx.setTextAlign('left')
  131. ctx.setFillStyle('#96999B')
  132. ctx.fillText(info.nickname.length > 11 ? info.nickname.slice(0, 11) + `${info.pinkId ? '...的团' : '...'}` : info.nickname + '的团', 35, 18)
  133. // 商品1图
  134. ctx.save();
  135. this.setRadius(ctx, 3, 0, 35, 211, 211)
  136. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  137. try {
  138. const { path } = await this.getImge(info.image)
  139. ctx.drawImage(path, 0, 35, 211, 211)
  140. } catch (error) {
  141. console.error(error);
  142. }
  143. ctx.restore();
  144. // 绿色背景
  145. ctx.setFillStyle('#19C161')
  146. ctx.fillRect(0, 130, 131, 40) // x, y, width, height
  147. ctx.setFontSize(16);
  148. ctx.setTextAlign('center')
  149. ctx.setFillStyle('#FFFFFF')
  150. ctx.fillText(${info.pinkId ? info.pinkPrice : info.price}起`, 65, 130 + 18)
  151. ctx.setFontSize(12);
  152. ctx.setTextAlign('center')
  153. ctx.setFillStyle('#FFFFFF')
  154. ctx.fillText(${info.otPrice}`, 65, 130 + 34)
  155. // 划线
  156. ctx.beginPath()
  157. ctx.setLineWidth(1);
  158. ctx.setStrokeStyle('#FFFFFF')
  159. ctx.moveTo(40, 130 + 30)
  160. ctx.lineTo(90, 130 + 30)
  161. ctx.stroke()
  162. // 深绿色背景
  163. ctx.setFillStyle('#19AF5C')
  164. ctx.fillRect(131, 130, 211 - 131, 40)
  165. if (info.pinkId) {
  166. // 立即跟团按钮
  167. ctx.setFontSize(16);
  168. ctx.setTextAlign('center')
  169. ctx.setFillStyle('#FFFFFF')
  170. ctx.fillText(`立即跟团`, 131 + (211 - 131) / 2, 130 + 26)
  171. } else {
  172. // 已团数量
  173. ctx.setFontSize(12);
  174. ctx.setTextAlign('center')
  175. ctx.setFillStyle('#FFFFFF')
  176. ctx.fillText(`已团 ${this.getSales(info.sales)} 件`, 131 + (211 - 131) / 2, 130 + 25)
  177. }
  178. ctx.draw(false, (() => {
  179. uni.canvasToTempFilePath({
  180. canvasId: 'shareCanvas',
  181. success: (res) => {
  182. return resolve(res.tempFilePath)
  183. },
  184. fail: function (error) {
  185. console.log('fail----fail', error);
  186. //TODO
  187. return reject(error)
  188. }
  189. }, this)
  190. }))
  191. } catch (error) {
  192. uni.hideLoading()
  193. console.log('画图失败error', error);
  194. return reject(error)
  195. }
  196. })
  197. },
  198. // 团分享
  199. setGroupShareCanvas(info) {
  200. console.log('团分享-info', info);
  201. return new Promise(async (resolve, reject) => {
  202. try {
  203. const ctx = uni.createCanvasContext('shareCanvas', this)
  204. // 绘制背景图
  205. ctx.setFillStyle('#FFFFFF')
  206. ctx.fillRect(0, 0, 211, 170)
  207. if (info.productImageList.length == 1) {
  208. // 团长头像
  209. ctx.save();
  210. this.setRadius(ctx, 5, 0, 0, 30, 30)
  211. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  212. try {
  213. const { path } = await this.getImge(info.avatar)
  214. ctx.drawImage(path, 0, 0, 30, 30)
  215. } catch (error) {
  216. console.error(error);
  217. }
  218. ctx.restore();
  219. // 团长昵称
  220. ctx.setFontSize(12);
  221. ctx.setTextAlign('left')
  222. ctx.setFillStyle('#96999B')
  223. ctx.fillText((info.nickname.length > 11 ? info.nickname.slice(0, 11) + '...' : info.nickname) + '的团', 35, 18)
  224. // 商品1图
  225. ctx.save();
  226. this.setRadius(ctx, 3, 0, 35, 211, 211)
  227. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  228. try {
  229. const { path } = await this.getImge(info.productImageList[0])
  230. ctx.drawImage(path, 0, 35, 211, 211)
  231. } catch (error) {
  232. console.error(error);
  233. }
  234. ctx.restore();
  235. }
  236. if (info.productImageList.length >= 2) {
  237. // 团长头像
  238. ctx.save();
  239. this.setRadius(ctx, 5, 0, 0, 42, 42)
  240. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  241. try {
  242. const { path } = await this.getImge(info.avatar)
  243. ctx.drawImage(path, 0, 0, 42, 42)
  244. } catch (error) {
  245. console.error(error);
  246. }
  247. ctx.restore();
  248. // 团长昵称
  249. ctx.setFontSize(12);
  250. ctx.setTextAlign('left')
  251. ctx.setFillStyle('#96999B')
  252. ctx.fillText((info.nickname.length > 10 ? info.nickname.slice(0, 10) + '...' : info.nickname) + '的团', 47, 25)
  253. // 商品1图
  254. ctx.save();
  255. this.setRadius(ctx, 3, 0, 51, 69, 69)
  256. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  257. try {
  258. const { path } = await this.getImge(info.productImageList[0])
  259. ctx.drawImage(path, 0, 51, 69, 69)
  260. } catch (error) {
  261. console.error(error);
  262. }
  263. ctx.restore();
  264. // 商品2图
  265. ctx.save();
  266. this.setRadius(ctx, 3, 69 + 2, 51, 69, 69)
  267. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  268. try {
  269. const { path } = await this.getImge(info.productImageList[1])
  270. ctx.drawImage(path, 69 + 2, 51, 69, 69)
  271. } catch (error) {
  272. console.error(error);
  273. }
  274. ctx.restore();
  275. if (info.productImageList.length >= 3) {
  276. // 商品3图
  277. ctx.save();
  278. this.setRadius(ctx, 3, 69 * 2 + 4, 51, 69, 69)
  279. ctx.clip(); // 画了圆 再剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内
  280. try {
  281. const { path } = await this.getImge(info.productImageList[2])
  282. ctx.drawImage(path, 69 * 2 + 4, 51, 69, 69)
  283. } catch (error) {
  284. console.error(error);
  285. }
  286. ctx.restore();
  287. }
  288. }
  289. // 绿色背景
  290. ctx.setFillStyle('#19C161')
  291. ctx.fillRect(0, 128, 211, 42) // x, y, width, height
  292. ctx.setFontSize(16);
  293. ctx.setTextAlign('center')
  294. ctx.setFillStyle('#FFFFFF')
  295. ctx.fillText(`立即跟团`, 211 / 2, 128 + 26)
  296. ctx.draw(false, (() => {
  297. uni.canvasToTempFilePath({
  298. canvasId: 'shareCanvas',
  299. success: (res) => {
  300. return resolve(res.tempFilePath)
  301. },
  302. fail: function (error) {
  303. console.log('fail----fail', error);
  304. //TODO
  305. return reject(error)
  306. }
  307. }, this)
  308. }))
  309. } catch (error) {
  310. console.log('画图失败error', error);
  311. return reject(error)
  312. }
  313. })
  314. },
  315. /**
  316. * 设置圆角矩形
  317. *
  318. * @param ctx 绘图上下文
  319. * @param cornerRadius 圆角半径
  320. * @param width 矩形宽度
  321. * @param height 矩形高度
  322. * @param x 矩形左上角的 x 坐标
  323. * @param y 矩形左上角的 y 坐标
  324. * @returns 无返回值
  325. */
  326. setRadius(ctx, cornerRadius, x, y, width, height) {
  327. // 开始绘制路径
  328. ctx.beginPath();
  329. // 绘制最左侧的圆角
  330. ctx.arc(x + cornerRadius, y + cornerRadius, cornerRadius, Math.PI, Math.PI * 1.5);
  331. // 绘制顶部边缘
  332. ctx.moveTo(x + cornerRadius, y);
  333. ctx.lineTo(x + width - cornerRadius, y);
  334. ctx.lineTo(x + width, y + cornerRadius);
  335. // 绘制最右侧的圆角
  336. ctx.arc(x + width - cornerRadius, y + cornerRadius, cornerRadius, Math.PI * 1.5, Math.PI * 2);
  337. // 绘制右侧边缘
  338. ctx.lineTo(x + width, y + height - cornerRadius);
  339. ctx.lineTo(x + width - cornerRadius, y + height);
  340. // 绘制最下侧的圆角
  341. ctx.arc(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0, Math.PI * 0.5);
  342. // 绘制底部边缘
  343. ctx.lineTo(x + cornerRadius, y + height);
  344. ctx.lineTo(x, y + height - cornerRadius);
  345. // 绘制最左侧的圆角
  346. ctx.arc(x + cornerRadius, y + height - cornerRadius, cornerRadius, Math.PI * 0.5, Math.PI);
  347. // 绘制左侧边缘
  348. ctx.lineTo(x, y + cornerRadius);
  349. ctx.lineTo(x + cornerRadius, y);
  350. // 闭合路径
  351. ctx.closePath();
  352. },
  353. // 获取图片地址
  354. getImge(path) {
  355. // 利用promise异步转同步,否则可能显示不了~
  356. return new Promise((resolve, reject) => {
  357. uni.getImageInfo({
  358. src: path,
  359. success: function (res) {
  360. if (res && res.path) {
  361. resolve(res)
  362. } else {
  363. reject(false)
  364. }
  365. },
  366. fail: function (res) {
  367. reject(res)
  368. }
  369. })
  370. })
  371. },
  372. getSales(sales) {
  373. return sales >= 10000 ? sales / 10000 + 'w+' : sales
  374. },
  375. }
  376. }
  377. </script>
  378. <style lang="scss" scoped>
  379. // 隐藏画布
  380. .ShareCanvas {
  381. position: absolute;
  382. top: -200px;
  383. z-index: -1;
  384. opacity: 0;
  385. .canvas canvas {
  386. width: 211px;
  387. height: 170px; // +16
  388. }
  389. }
  390. </style>

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

onShareAppMessage 方法的内容:

注意:

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

setOrderCanvas()方法的样式 

 

setGoodsShareCanvas()方法的样式

setGroupShareCanvas()方法的样式

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/936439
推荐阅读
相关标签
  

闽ICP备14008679号