先下载依赖
npm install --save wxml-to-canvas
下好后在node_modules中找到,创建一个wxcomponents文件夹,将这两个文件放进去。
将module.exports = require("widget-ui“);改为module.exports = require("../../widget-ui/dist/index.js");
然后在全局插件引入
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "啊实打实的",
"navigationBarBackgroundColor": "#fff5ff",
"backgroundColor": "#F8F8F8",
"usingComponents": {
"wxml-to-canvas": "/wxcomponents/wxml-to-canvas/src/index"
}
},
ok 配置完成,接下来是在页面模板中写入
<wxml-to-canvas class="widget"></wxml-to-canvas>
然后封装mixin混入
// myMixin.js
const {
wxml,
style
} = require('./notification.js')
export default {
data() {
return {
canvasWidth: 320, // 默认canvas宽高
canvasHeight: 480,
canvasScreenWidth: null, // 设备宽度
canvasScreenHeight: null, // 设备宽度
canvasControlContent: undefined,
//上个页面用到的图片地址
canvasTempFile: undefined
}
},
methods: {
async loadFun() {
// 获取设备信息
await wx.getSystemInfo({
success: (res) => {
console.log('res.canvasScreenWidth', res);
this.canvasScreenWidth = res.screenWidth
this.canvasScreenHeight = 800 //高度建议计算得出或写死。如使用res.screenHeight,文字过长时无法生成(安卓手机,最新鸿蒙系统高度不能超过1000)
this.canvasWidth = this.canvasScreenWidth
this.canvasHeight = this.canvasScreenHeight
setTimeout(() => {
console.log('this', this);
console.log("this.selectComponent('.widget')", this.selectComponent('.widget'));
this.widget = this.selectComponent('.widget');
this.canvasControlContent = '你好呀';
this.canvasTempFile = 'https://img0.baidu.com/it/u=1715545265,2854041546&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=234'
this.download();
}, 1000)
}
});
},
//生成图片
download() {
// 数字容器宽度 动态设置
setTimeout(() => {
uni.showLoading({
title: '图片生成中...'
})
this.renderToCanvas()
}, 1000)
},
renderToCanvas() {
const _wxml = wxml(this.chosen_chat_list) //调用wxml
console.log('_wxml', _wxml);
const _style = style(this.canvasScreenWidth, this.canvasWidth, this.canvasHeight)
setTimeout(() => {
console.log('this.widget', this.widget);
const p1 = this.widget.renderToCanvas({
wxml: _wxml,
style: _style
})
p1.then((res) => {
console.log('生成成功', res);
uni.hideLoading()
this.saveImageToPhotosAlbum();
}).catch((err) => {
console.log('生成失败')
})
}, 100)
},
//保存图片到本地
saveImageToPhotosAlbum() {
uni.showLoading({
title: '正在保存中...'
})
const p2 = this.widget.canvasToTempFilePath()
console.log('p2', p2);
let that = this
p2.then(result => {
let path = result.tempFilePath
console.log('path', path);
uni.saveImageToPhotosAlbum({
filePath: path,
success: () => {
uni.hideLoading()
uni.showToast({
title: '保存成功,可去手机相册查看',
duration: 2000,
icon: 'none'
});
uni.navigateBack();
}
});
})
}
}
}
let aaa = {
data() {
return {
canvasW: 0, // 画布宽
canvasH: 0, // 画布高
canvasSystemInfo: {}, // 设备信息
canvasGoodsImg: {}, // 商品主图信息
canvasEwmW: 140, // 二维码大小
canvasTitle: '商品标题商品标题商品标题', // 商品标题
canvasPrice: '4158.00', // 价格
canvasName: '浪迹天涯', // 推荐人
canvasIsShow: false,
domContent: `
<div id="source-element">
<h1 style="color: blue; text-align: center;">这是一个标题</h1>
</div>
`,
};
},
methods: {
async loadFun() {
// 获取设备信息,主要获取宽度,赋值给canvasW 也就是宽度:100%
this.canvasSystemInfo = await this.getSystemInfo();
console.log(this.canvasSystemInfo, '我是设备宽度');
this.canvasTitle = '我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题我是标题';
this.canvasPrice = '价格价格价格';
// 获取主图,二维码信息
// let goodsImgUrl = option.goosImg // 主图本地路径,也可以用网络地址
let goodsImgUrl = 'http://img.touxiangwu.com/2020/3/uq6Bja.jpg' // 头部封面图片
// shareCodeGet(param).then(res => { //调接口
// console.log(res, 'resres')
// let ewmImgUrl = res.data.msg; //二维码图片地址-从接口取
// 以下内容放接口里面---下面的内容放接口里面-主要返回二维码地址
// })
let that = this
uni.downloadFile({
url: goodsImgUrl,
success: async function (logoRes) {
goodsImgUrl = logoRes.tempFilePath
var ewmImgUrl = require('@/static/AIFlower.png')
// uni.downloadFile({
// url: ewmImgUrl,
// success: function (logoRes) {
// ewmImgUrl = logoRes.tempFilePath
that.canvasW = that.canvasSystemInfo.windowWidth; // 画布宽度
// 画布高度 = 主图高度+二维码高度 + 文字图片的间距(大概50)
that.canvasH = 580 //this.canvasGoodsImg.height + this.canvasEwmW + 10;
uni.showToast({
icon: 'loading',
mask: true,
duration: 10000,
title: '海报绘制中',
});
setTimeout(() => {
var ctx = uni.createCanvasContext('myCanvas', that);
//列表部分
var fontSize = 12;
var itemHeight = fontSize + 5;
var x = 5;
var y = fontSize;
ctx.font = fontSize + "px Arial";
console.log(' this.chosen_chat_list', that.chosen_chat_list);
that.chosen_chat_list.forEach(item => {
// 绘制列表项
ctx.fillText(item.content, x, y);
// 移动到下一个位置
y += itemHeight;
});
//底部
ctx.setFontSize(14)
ctx.setFillStyle('#b8b8b8')
ctx.fillText(
'长按或扫描识别二维码' || '', that.canvasW / 2 - that.canvasW / 2.4 / 2,
that.canvasW + that.canvasEwmW + 10);
// draw方法 把以上内容画到 canvas 中
ctx.draw(true, (ret) => {
that.canvasIsShow = true // 显示按钮-保存图片到相册
uni.showToast({
icon: 'success',
mask: true,
title: '绘制完成',
});
uni.canvasToTempFilePath({ // 保存canvas为图片
canvasId: 'myCanvas',
quality: 1,
complete: function (res) {
// 在H5平台下,tempFilePath 为 base64, // 图片提示跨域 H5保存base64失败,APP端正常输出临时路径
console.log(res.tempFilePath, '66666666')
// uni.setStorageSync('filePath', res.tempFilePath) //保存临时文件路径到缓存
},
})
});
}, 1500)
// },
// fail:(err)=>{
// console.log('图片下载失败',err);
// }
// })
}
})
},
async drawDomToCanvas() {
// 创建一个不可见的容器并插入DOM内容
const hiddenContainer = document.getElementById("hidden-container");
this.domContent = `<view id="source-element" class="chat-body">
<!-- 聊天记录 -->
<view class="chat-item" v-for="(item, index) in msgList" :key="index" :id="'index-' + index">
<!-- 自己发的消息 -->
<view class="item self" v-if="item.sender == 'user'">
<!-- 文字内容 -->
<view class="content right">
<!-- 文件列表 -->
<view v-if="item.files && item.files.length !== 0">
<my-album :FileList="item.files"></my-album>
<my-files :FileList="item.files" />
</view>
<my-text :text="item.content" />
</view>
</view>
<!-- 机器人发的消息 -->
<view class="item Ai" v-if="item.sender == 'bot'">
<!-- 头像 -->
<view class="ai-avatar" v-if="msgList[index-1].sender != 'bot'">
<image class="avatar" src="@/static/img/head_ai.png"></image>
<view class="text">小花助手</view>
</view>
<!-- 文字内容 -->
<view class="content left">
<view v-if="item.files && item.files.length !== 0">
<my-loading :FileList="item.files"></my-loading>
<my-album :FileList="item.files"></my-album>
<my-files :FileList="item.files" />
</view>
<my-text :loading="loading" :checkboxShow="checkboxShow" @checkFun="checkFun" @clickHandleSend="handleSend" :text="item.content" />
<Animation v-show="item.loading" />
</view>
</view>
</view>
</view>`
// this.chosen_chat_list.forEach(item => {
// // 绘制列表项
// ctx.fillText(item.content, x, y);
// // 移动到下一个位置
// y += itemHeight;
// });
hiddenContainer.innerHTML = this.domContent;
// 获取源元素
const sourceElement = document.getElementById("source-element");
// 使用html2canvas将源元素转换为Canvas图像
const canvas = await html2canvas(sourceElement);
// 将转换后的Canvas图像绘制到目标Canvas上
ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height);
},
// 获取图片信息
getImageInfo(image) {
return new Promise((req, rej) => {
uni.getImageInfo({
src: image,
success: function (res) {
req(res)
},
});
})
},
// 获取设备信息
getSystemInfo() {
return new Promise((req, rej) => {
uni.getSystemInfo({
success: function (res) {
req(res)
}
});
})
},
// 保存图片到相册
async saveImage() {
let filePath = uni.getStorageSync('filePath') //从缓存中读取临时文件路径
console.log(filePath, "filePath")
uni.saveImageToPhotosAlbum({
filePath: filePath,
success() {
uni.showToast({
title: '图片保存成功',
icon: 'none'
})
},
fail(e) {
uni.showToast({
title: '图片保存失败',
icon: 'none'
})
}
})
}
},
};
然后是封装notification.js
aaa变量是原本别人的方法,上面是我自己写的,目前还有问题,插件官网wxml-to-canvas | 微信开放文档,需要注意样式问题
对象属性值为对应 wxml 标签的 class 驼峰形式。需为每个元素指定 width 和 height 属性,否则会导致布局错误。
存在多个 className 时,位置靠后的优先级更高,子元素会继承父级元素的可继承属性。
元素均为 flex 布局。left/top 等 仅在 absolute 定位下生效
const wxml = (msgList) => {
// let wml = `<view class="chatBody">
// ${msgList.map((item, index) => {
// if (item.sender == 'user') {
// return `<view class="item self" >
// <view class="content right">
// <view>${item.content}</view>
// </view>
// </view>`
// } else if (item.sender == 'bot') {
// return `<view class="item Ai">
// ${(index==0||msgList[index - 1].sender != 'bot' )&& `<view class="aiAvatar">
// <image class="avatar" src="@/static/img/head_ai.png"></image>
// <view class="text">小花助手</view>
// </view>`}
// <view class="content left">
// <view>${item.content}</view>
// </view>
// </view>`
// }
// })}
// </view>`
let wml = `<view class="chatBody">
${msgList.map((item, index) => {
return item.content
})}
</view>`
console.log('wml', wml);
return wml
}
// ${msgList.map((item, index) => {
// if (item.sender == 'user') {
// return `<view class="item self" >
// <view class="content right">
// <view>${item.content}</view>
// </view>
// </view>`
// } else if (item.sender == 'bot') {
// return `<view class="item Ai">
// ${(index==0||msgList[index - 1].sender != 'bot' )&& `<view class="ai-avatar">
// <image class="avatar" src="@/static/img/head_ai.png"></image>
// <view class="text">小花助手</view>
// </view>`}
// <view class="content left">
// <view>${item.content}</view>
// </view>
// </view>`
// }
// })}
let qita = `
<view v-if="item.files && item.files.length !== 0">
<my-album :FileList="item.files"></my-album>
<my-files :FileList="item.files" />
</view>
<view v-if="item.files && item.files.length !== 0">
<my-album :FileList="item.files"></my-album>
<my-files :FileList="item.files" />
</view>
`
/**
* @param {*} screenWidth 屏幕宽度
* @param {*} canvasWidth 画布宽度
* @param {*} canvasHeight 画布高度
* @param {*} numberWidth 数字宽度,动态设置
* @return {*}
*/
const style = (screenWidth, canvasWidth, canvasHeight) => {
return {
"chatBody": {
width: screenWidth,
height: canvasHeight,
// backgroundColor: `linear-gradient(
// 145deg,
// rgba(76, 216, 251, 0.5) 0%,
// rgba(255, 255, 255, 0.5) 35%,
// rgba(255, 255, 255, 0.5) 26%,
// rgba(254, 235, 255, 0.5) 50%
// )
// bottom right / 50% 50% no-repeat,
// linear-gradient(
// 215deg,
// rgba(76, 216, 251, 0.5) 0%,
// rgba(255, 255, 255, 0.5) 35%,
// rgba(255, 255, 255, 0.5) 26%,
// rgba(254, 235, 255, 0.5) 50%
// )
// bottom left / 50% 50% no-repeat,
// linear-gradient(
// 325deg,
// rgba(76, 216, 251, 0.5) 0%,
// rgba(255, 255, 255, 0.5) 35%,
// rgba(255, 255, 255, 0.5) 26%,
// rgba(254, 235, 255, 0.5) 50%
// )
// top left / 50% 50% no-repeat,
// linear-gradient(
// 35deg,
// rgba(76, 216, 251, 0.5) 0%,
// rgba(255, 255, 255, 0.5) 35%,
// rgba(255, 255, 255, 0.5) 26%,
// rgba(254, 235, 255, 0.5) 50%
// )
// top right / 50% 50% no-repeat,
// #fff`,
backgroundColor:"#fff",
color: '#333',
fontSize: 14
},
"item": {
boxSizing: 'border-box',
width: screenWidth,
height: screenWidth*0.3,//测试
padding: '12rpx 20rpx',
display: 'flex',
flexWrap: 'wrap'
},
"self": {
justifyContent: 'flex-end',
flex: '1'
},
"aiAvatar": {
width: screenWidth,
height: screenWidth*0.3,//测试
display: 'flex',
},
"avatar": {
width: screenWidth*0.1,
height: screenWidth*0.1,
display: 'flex',
justifyContent: 'center',
borderRadius: '50%',
overflow: 'hidden',
margin: '0 6px 6px 0'
},
"text": {
width: screenWidth,
height: screenWidth*0.1,
lineHeight: screenWidth*0.1,
fontSize: 14,
background: 'linear-gradient(90deg, #fff 0%, #f306fb 0.01%, #0098f0 93.75%)',
webkitBackgroundClip: 'text',
webkitTextFillColor: 'transparent',
},
"content": {
width: screenWidth,//测试
height: screenWidth*1,//测试
display: 'inline-block',
wordWrap: 'break-word',
padding: '12px',
borderRadius: '16px',
fontSize: 14,
fontFamily: 'PingFang SC',
fontWeight: '400',
color: '#333333',
lineHeight: '20px'
},
"left": {
backgroundColor: '#ffffff',
borderTopLeftRadius: ' 4px !important'
},
"right": {
backgroundColor: '#c2dcff',
borderTopRightRadius: '4px !important'
},
// "container-mm": {
// width: canvasWidth,
// height: canvasHeight,
// position: 'relative',
// overflow: 'hidden',
// backgroundColor: '#ffffff',
// padding: '30rpx 20rpx',
// },
// "name": {
// fontSize: 20,
// color: '#333',
// marginLeft: canvasWidth * 0.08,
// width: canvasWidth * 0.84,
// height: screenWidth * 0.18,
// textAlign: 'center',
// },
// "content": {
// fontSize: 14,
// color: '#333',
// width: canvasWidth * 0.84,
// height: screenWidth * 0.84,
// marginLeft: canvasWidth * 0.08,
// marginTop: canvasWidth * 0.08,
// },
// "pic": {
// width: canvasWidth * 0.4,
// height: screenWidth * 0.2,
// marginTop: canvasWidth * 0.1,
// marginLeft: canvasWidth * 0.35,
// marginBottom: canvasWidth * 0.05,
// borderRadius: screenWidth * 0.14,
// overflow: 'hidden',
// },
}
}
module.exports = {
wxml,
style
}
在页面中使用 混入方法
import sharrChatRecord from "./sharrChatRecord.js";
mixins: [sharrChatRecord],