一、需求
项目中要实现一个将图片分享到朋友圈的功能,将生成的海报转成图片保存到手机。用到了wxml-to-canvas插件。
二、官方示例使用方法
1.安装wxml-to-canvas
npm install --save wxml-to-canvas
2.JSON 组件声明
{ "usingComponents": { "wxml-to-canvas": "wxml-to-canvas", }}
3.wxml 引入组件
<video class="video" src="{{src}}"> <wxml-to-canvas class="widget"></wxml-to-canvas></video><image src="{{src}}" style="width: {{width}}px; height: {{height}}px"></image>
4. js 获取实例
const {wxml, style} = require('./demo.js')Page({ data: { src: '' }, onLoad() { this.widget = this.selectComponent('.widget') }, renderToCanvas() { const p1 = this.widget.renderToCanvas({ wxml, style }) p1.then((res) => { this.container = res this.extraImage() }) }, extraImage() { const p2 = this.widget.canvasToTempFilePath() p2.then(res => { this.setData({ src: res.tempFilePath, width: this.container.layoutBox.width, height: this.container.layoutBox.height }) }) }})
三、实际项目
安装、json配置、wxml引入都一样。
1.wxml
<wxml-to-canvas class="widget" width="325" height="550"></wxml-to-canvas> <view class='save flex-center-center' bindtap='preservation'> 保存海报 </view>
2.js文件
const net = require('../../../common/network.js');//⚠️海报内容和样式const { wxml, style } = require('./canvas.js');//自己封装的微信apiimport wxApi from '../../../common/wxApi';const app = getApp();Page({ /** * 页面的初始数据 */ data: { }, /** * 生命周期函数--监听页面加载 */ onLoad(options) { wx.showLoading({ title: '海报生成中...', }); //获取页面初始数据 this.getServerData(); }, getServerData() { net.posterInfo().then((response) => { const { name, title, teacher, qr_code, task_id } = response.data; const { real_name, avatarurl, nickname } = app.globalData.userInfo; //画海报用到的数据 this.setData({ info: response.data, avatarurl, name, title, teacher, qr_code, }); //注意⚠️:这里是对页面初始渲染 this.widget = this.selectComponent('.widget'); const _wxml = wxml(name, avatarurl, title, teacher, qr_code); //onload方法里节点没加载完,设置定时器 setTimeout(() => { //渲染到 canvas,传入 wxml 模板 和 style 对象,返回的容器对象包含布局和样式 //信息。 const p1 = this.widget.renderToCanvas({ wxml: _wxml, style, }); p1.then((res) => { this.container = res; wx.hideLoading(); }); }, 500); }); }, preservation() { // this.widget = this.selectComponent('.widget') const { task_id } = this.data; const p2 = this.widget.canvasToTempFilePath(); p2.then((res) => { //保存到本地相册 wxApi.apiScopeOauth('scope.writePhotosAlbum').then(() => { wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success(res) { util.showToast( '海报已保存,快去朋友圈分享吧!', 'none', 3000 ); }, fail(res) { wx.showToast({ icon: 'error', title: '保存图片失败!', }); }, }); }); }).catch((fail) => { wx.showToast({ icon: 'error', title: '请稍后再试', }); }); },});
3.canvas.js
只展示大体框架,具体内容涉及隐私去掉了。
wxml返回的是页面内容的字符串。
const wxml = ( name, avatarurl,title,teacher,qr_code ) => { return ` <view class="poster-wapper"> <image class="poster-img" src="https://1.png"/> <view class="author"> <text class="author-text">作者:${name}</text> </view> <view class="head"> <view class="head-border"> <image class="head-img" src="${avatarurl}"></image> </view> </view> <view class="poster-info"> <view class="info"> <text class="title">挑战内容:《${title}》</text> `+ (teacher?`<text class="teacher">老师:${teacher}</text>`:'') +` <view class="line"></view> <text class="tip">本次朗读近乎完美!快来听听吧!</text> </view> <image class="qrcode-img" src="${qr_code}" /> </view> </view> `}const style = { posterWapper:{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', position: 'relative', }, posterImg: { width: 325, height: 550 }, author: { width: 119, height: 32, borderRadius: 12, backgroundColor: 'rgba(255,255,255,0.8)', position: 'absolute', paddingLeft: 18, top: 353, left: 45, }, authorText: { width: 98, height: 32, paddingLeft: 13.5, fontSize: 11, fontWeight: 600, color: '#333333', verticalAlign: 'middle', }, head: { width: 51, height: 51, borderRadius: 25.5, display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'absolute', top: 343, left: 12, backgroundColor: 'rgba(255,255,255,0.8)', }, headBorder: { width: 46, height: 46, backgroundColor: 'rgba(255,255,255)', borderRadius: 23, display: 'flex', alignItems: 'center', justifyContent: 'center', }, headImg: { width: 43, height: 43, borderRadius: 20, }, posterInfo:{ position: 'absolute', bottom: -6, width: 325, height: 143, backgroundColor: '#FFFFFF', borderRadius: 10, display: 'flex', flexDirection: 'row' }, info: { width: 210, height: 140, display: 'flex', flexDirection: 'column', justifyContent: 'center', paddingTop: 6, paddingLeft: 15 }, title: { width: 210, height: 24, fontSize: 13, fontWeight: 600, color: '#333333', lineHeight: 24 }, teacher:{ width: 210, height: 24, fontSize: 13, fontWeight: 400, color: '#666666', lineHeight: 24, }, line:{ width: 190, height: 1, backgroundColor: 'rgba(3,0,0,0.16)' }, tip: { height: 24, width: 210, fontSize: 13, fontFamily: 'PingFang SC', fontWeight: 600, color: '#333333', lineHeight: 24, }, btn:{ width: 140, height: 33, backgroundColor: '#FF6000', borderRadius: 19, lineHeight: 33, textAlign: 'center', marginTop: 7.5, position: 'relative' }, btnText:{ width: 140, height: 33, fontSize: 18, fontFamily: 'PingFang SC', fontWeight: 600, color: '#FFFFFF', }, btnImg:{ width:37, height:18, position: 'absolute', left: 150, top: 9 }, qrcodeImg:{ width: 92, height: 92, marginLeft: 5, position: 'absolute', bottom: 33, right: 12 }}module.exports = { wxml, style}
注意点⚠️:
1.wxml支持 view
、text
、image
三种标签,通过 class 匹配 style 对象中的样式。
2.css对象属性值为对应 wxml 标签的 class 驼峰形式。需为每个元素指定 width 和 height 属性,否则会导致布局错误。
3.存在多个 className 时,位置靠后的优先级更高,子元素会继承父级元素的可继承属性。
元素均为 flex 布局。left/top 等 仅在 absolute 定位下生效。
4.css不支持背景图片,在wxml中用img代替。
5.在写text标签时外边必须套一层view标签,否则不显示。(这一点是我遇到的问题,不知道是不是这样规定)
补充:(2022/12/15)
背景图片在开发工具和开发版都可以正常显示,但是在体验版显示不出来;
图片的域名在项目配置里没有,在后台管理-开发设置-服务器域名-request合法域名-配置图片的域名即可正常显示。