业务场景
二轮充电业务中,用户充电实现后在订单详情页展现订单相干信息,用户点击分享按钮唤起微信小程序分享菜单,将生成的图片海报分享给微信好友或者下载到本地,好友可通过扫描海报中的二维码加群支付优惠。
应用场景及性能:微信小程序 生成海报图片 分享好友 下载图片
应用技术:Taro vue vant canvas
实现效果图
重点步骤拆分
1、封装一个海报分享组件 poster-share.vue
2、用 canvas 画图,将背景图、费用、二维码等信息绘制在一张图上,其中费用、二维码是动静获取的
3、生成一张本地缓存图片
4、唤起微信分享性能,实现分享和下载性能
重点步骤有了,那么就开干吧!
外围代码实现
1、模版局部
须要一个画布 dom 用来绘制图片,一个用来寄存生成图片的 dom
问:canvasId 为什么须要动静生成呢?
答:防止一个页面中应用多个组件引起的 canvasId 反复问题
<template>
<div class="poster-share__content">
<!-- canvas 生成的海报图片 -->
<img
v-if="posterImg"
class="poster-share__content--img"
mode="aspectFit"
:src="posterImg"
>
<!-- 分享海报 canvas 绘制局部 -->
<canvas
class="poster-share__content--cvs"
:canvas-id="canvasId"
></canvas>
</div>
</template>
2、款式局部
该业务场景下,不能让用户看到画布,然而设置 canvas 的 display 为 none 将不能进行绘制,会报如下谬误,导致绘制失败。
实现形式:采纳定位的形式,将 canvas 定位到可视区域外,具体代码如下。
.poster-share__content {
position: absolute;
right: -9999px;
top: -9999px;
width: 560px;
height: 852px;
opacity: 0;
z-index: -1;
&--img {
width: 100%;
height: 100%;
}
&--cvs {
width: 100%;
height: 100%;
}
}
3、外围 js 局部
开始写外围实现啦~
父组件传参管制子组件是否开始绘制,子组件绘制实现后告诉父组件扭转状态。
name: 'CpPosterShare',
model: {
prop: 'value',
event: 'update:value',
},
props: {
value: {
type: Boolean,
default: false,
},
config: {
type: Object,
default: () => ({}),
},
},
data () {
return {
isDraw: false, // 是否开始绘制海报
posterImg: '', // 生成的海报图片地址
canvasId: `canvasId${Math.random() }`,
screenWidth: null, // 屏幕宽度
}
},
watch: {
value: {handler (val) {this.isDraw = val},
immediate: true,
},
isDraw (val) {this.$emit('update:value', val)
if (val) {this.init()
}
},
},
首先,咱们做的是一个小程序,将图片放在小程序源码中会加大包的体积,须要从网络上下载图片,因而须要封装一个公共的办法来获取图片的信息。Taro 提供 getImageInfo 办法返回图片的原始宽高、本地门路等信息。
// 加载图片
loadImg (src) {return newPromise((resolve, reject) => {
Taro.getImageInfo({src,}).then((res) => {resolve({ ...res})
}).catch((err) => {reject(err)
})
})
}
该业务场景中波及绘制多张图片,包含背景图片和二维码图片,须要将多张图片都 load 实现后能力开始绘制。
const promiseParams = [this.loadImg(BgImage), this.loadImg(QRcode)]
const promiseAll = Promise.all(promiseParams.map((item) =>item.catch(() =>null)))
promiseAll.then((res) => {this.draw(res)
}).catch((err) => {console.log(err)
})
开始绘制啦~
创立 canvas 绘图上下文 CanvasContext 对象,调用 Taro 提供的办法 Taro.createCanvasContext(canvasId) 绘制背景图、绘制价格、绘制二维码,这里就不一一赘述了。全副绘制实现后,将画布中的内容导出生成图片,Taro 提供了 canvasToTempFilePath 办法,须要在 draw() 回调中调用能力保障图片导出胜利,返回生成图片的长期门路。
ctx.draw(false, () => {
Taro.canvasToTempFilePath({canvasId:this.canvasId,}).then((res) => {
this.posterImg = res.tempFilePath
// 唤起分享菜单
this.showShareImageMenu()}).catch((err) => {console.log('海报生成失败', err)
Taro.showToast({
title: '海报生成失败',
icon: 'error',
})
}).finally(() => {Taro.hideLoading()
this.isDraw = false
})
})
本地图片生成胜利后,唤起微信提供的分享菜单弹窗,能够将图片发送给敌人、珍藏、保留到相册。Taro 提供 showShareImageMenu 办法唤起分享菜单弹窗,入参为本地图片门路。
showShareImageMenu () {if (Taro.showShareImageMenu) {
Taro.showShareImageMenu({path:this.posterImg,}).then().catch((err) => {console.log(err)
const {errMsg} = err
// 勾销操作 errMsg === 'showShareImageMenu:fail cancel'
// 回绝受权 errMsg: "showShareImageMenu:fail auth deny"
if (errMsg === 'showShareImageMenu:fail auth deny') {
authorize({
scope:'writePhotosAlbum',
showModal:true,
authName:'保留图片到相册',
success: () => {this.downloadImg()
},
})
}
}).finally(() => {this.isDraw = false})
} else {
Taro.showToast({
title:'小程序版本不反对该性能',
icon:'error',
})
}
}
用户点击发送给敌人,会调起微信对话框,将生成的海报图片粘贴分享给敌人;
点击珍藏,会将海报图片增加到珍藏列表中,不便下次查看;
点击保留到相册,会唤起保留图片受权弹窗,用户点击容许,会将海报图片保留在本地相册中。
如果用户在保留图片受权弹窗中第一次点击回绝,之后再次点击分享下载时,须要有受权提醒弹窗,提醒用户是否关上设置去受权,具体展现如下。
Taro 提供 Taro.openSetting 办法调起小程序设置页面,用户开启“增加到相册”受权后胜利后,调用 Taro 提供的下载图片的办法 Taro.saveImageToPhotosAlbum 将图片下载到本地。
受权提醒弹窗 | 设置页面 | 下载提醒 |
其中判断用户是否开启受权的办法具体实现如下:
/**
* 权限获取流程
* @param scope 权限英文名称
* @param success 受权胜利的回调
* @param fail 受权失败的回调
* @param showModal 受权失败是否展现对话框提醒
* @param authName 受权失败是否展现对话框提醒展现的受权名称
* // 例子:开启用户的相册权限
authorize({
scope: 'writePhotosAlbum',
showModal: true,
authName: '保留图片到相册',
success () {console.log('受权胜利')
},
})
*/
export async function authorize (options) {
const {scope, success, fail, showModal = false, authName = '',} = options
try {const scopeName = `scope.${ scope}`
const auth = await Taro.getSetting()
if (!auth.authSetting[scopeName]) {Taro.authorize({ scope: scopeName}).then((res) => {if (res.errMsg === 'authorize:ok' && success) success()}, () => {if (showModal && authName) {
Taro.showModal({
title: '受权提醒',
content: ` 您回绝了 ${authName} 权限,是否关上设置去受权?`,
}).then((res) => {if (res.confirm) {Taro.openSetting().then((res2) => {if (res2.authSetting[scopeName] && success) {success()
} else if (fail) {fail()
}
})
} else {fail && fail()
}
}).catch((res) => {fail && fail(res)
})
} else {fail && fail()
}
})
} else {success && success()
}
} catch (err) {fail && fail(err)
}
}
至此,具体实现完结撒花~ 能够将组件用到页面中了
组件援用
<poster-share
v-model="draw" // 是否开始绘制海报海报
config="config" // 海报配置信息
/>
问题记录
在开发过程中遇到了一些问题,记录一下
景象:点击分享,生成 canvas 图片。开发者工具上每次都失常,ios 机每次都失常,局部安卓机每次都失常,局部安卓机,点击分享之后勾销,操作屡次,有几次会生成图片失败
报错信息:”errMsg”: “canvasToTempFilePath:fail :create bitmap failed”
谬误定位解决:canvas 须要始终显示,不能有 display:none 的状况
作者:京东批发 张梦雨
内容起源:京东云开发者社区