项目中要实现一个将图片分享到朋友圈的功能,将生成的海报转成图片保存到手机。用到了wxml-to-canvas插件。
npm install --save wxml-to-canvas
- {
- "usingComponents": {
- "wxml-to-canvas": "wxml-to-canvas",
- }
- }
- <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>
- 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引入都一样。
- <wxml-to-canvas class="widget" width="325" height="550">wxml-to-canvas>
-
- <view class='save flex-center-center' bindtap='preservation'>
- 保存海报
- view>
- 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: '请稍后再试',
- });
- });
- },
- });
只展示大体框架,具体内容涉及隐私去掉了。
wxml返回的是页面内容的字符串。
- const wxml = ( name, avatarurl,title,teacher,qr_code ) => {
- return `
-
-
-
-
作者:${name} -
-
-
-
${avatarurl} "> -
-
-
-
-
-
挑战内容:《${title}》 - `+ (teacher?`
老师:${teacher} `:'') - +`
-
本次朗读近乎完美!快来听听吧! -
-
${qr_code} " /> -
-
- `
- }
-
- 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标签,否则不显示。(这一点是我遇到的问题,不知道是不是这样规定)