一、需求
项目中要实现一个将图片分享到朋友圈的功能,将生成的海报转成图片保存到手机。用到了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');
//自己封装的微信api
import 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=""/>
<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合法域名-配置图片的域名即可正常显示。