需求:需要的收款码可以保存下来,使用直接使用canvas canvas进行绘制生成即可,h5使用html2canvas将dom元素直接转为图片即可
效果图:
完整代码:
wxml文件:
- 定义了一个
view
元素作为要转换的 DOM 元素,以及一个按钮用于触发转换操作。- 定义了一个
canvas
元素,用于绘制 DOM 元素内容。- 定义一个
image
元素,用于显示生成的图片。
<!--shared_store/pages/collectionCode/collectionCode.wxml-->
<view class="code_box">
<view class="collectionCode" id="shareView">
<image class="check_img" mode="widthFix" src="{{http_host}}/shared_store/web/static/images/check.png" alt="" ></image>
<view class="check_text">支持使用微信到店扫码买单</view>
<view class="can_box">
<image style="width:400rpx;height:400rpx;;margin-top: 30rpx;" src="{{qr_code_img}}" mode="widthFix"></image>
<view class="store_name">{{shop_name}}</view>
<view class="software_pay">
<image class="wechat_img" mode="widthFix" src="{{http_host}}/shared_store/web/static/images/wechat_pay.png" alt="" ></image>
<!-- <image class="check_img" mode="widthFix" src="{{http_host}}/shared_store/web/static/images/alipay.png" alt="" ></image> -->
</view>
</view>
</view>
<view class="save_btn" bindtap="saveFun">
<view class="btns">保存到相册</view>
</view>
</view>
<canvas canvas-id="my-canvas" style="width: {{can_width}}px; height: {{can_height}}px;"></canvas>
<image mode="widthFix" wx:if="{{false}}" src="{{imagePath}}" style="width:{{can_width}}px; height:{{can_height}}px"></image>
wxss
/* shared_store/pages/collectionCode/collectionCode.wxss */
page {
overflow: hidden;
}
view{
box-sizing: border-box;
}
.code_box{
width: 100%;
height: 100vh;
background-color: #112E98;
position: relative;
}
.collectionCode {
padding: 120rpx 40rpx;
width: 100%;
/* height: 100vh; */
/* padding-top: 256rpx; */
}
.collectionCode>view {
width: 100%;
height: 100%;
background: #fff;
border-radius: 20rpx;
/* padding: 50rpx; */
font-size: 28rpx;
color: #333;
text-align: center;
/* display: flex;
flex-direction: column;
align-items: center;
justify-content: center; */
box-sizing: border-box;
}
.collectionCode>view image {
width: 414rpx;
height: 414rpx;
margin-top: 30rpx;
}
.qr_box{
display: none;
}
.check_img{
width: 520rpx;
position: relative;
left: 50%;
transform: translateX(-50%);
}
.check_text {
overflow-wrap: break-word;
color: #fff!important;
font-size: 30rpx;
font-family: PingFangSC-Regular;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 42rpx;
padding-top: 22rpx;
margin-bottom: 60rpx;
background-color: transparent!important;
}
.can_box{
width: 100%;
padding: 60rpx 0 120rpx;
}
.store_name {
width: 100%;
font-size: 36rpx;
color: #112E98;
line-height: 46rpx;
text-align: center;
font-weight: 600;
white-space: pre-line;
padding: 30rpx 40rpx 0;
word-break: break-all;
}
.wechat_img{
width: 212rpx!important;
}
.save_btn {
position: fixed;
width: 100%;
padding: 20rpx 30rpx;
left: 0;
bottom: 0;
background: #fff!important;
z-index: 9999!important;
}
.btns {
/* background: #2E4985; */
font-size: 32rpx;
color: #2E4985;
text-align: center;
/* width: 100%; */
border-radius: 50rpx;
height: 80rpx;
line-height: 80rpx;
border: 1rpx solid #2E4985;
z-index: 9999!important;
}
json
{
"navigationBarTitleText": "收款二维码",
"navigationBarBackgroundColor":"#112E98",
"navigationBarTextStyle":"white",
"usingComponents": {}
}
js文件:
- 使用
wx.createSelectorQuery
来获取#my-element
的位置信息和样式信息。- 使用
wx.createCanvasContext('my-canvas')
创建画布上下文。- 通过
setFillStyle
、fillRect
、setFontSize
、setTextAlign
和fillText
方法将#my-element
的内容绘制到canvas
元素上。- 使用drawRoundedRect绘制一个圆角矩形、drawImage绘制图片,调用
ctx.draw()
将绘制指令发送到canvas
进行渲染。- 使用
wx.canvasToTempFilePath
将绘制完成的 Canvas 转换为图片,并将图片路径存储在页面数据中,以便在image
标签中显示。
这里使用了绘制圆角矩形的函数方法drawRoundedRect(注意:使用ctx.arcTo这里安卓有右上角圆角可能有问题,ios则没有,所以这里使用ctx.arc)、线上图片地址下载到本地转换成临时文件路径方法unloadPic、换行显示处理事件breakText。如果用的线上图片转换成临时路径文件,在报错时不要使用下载方法wx.downloadFile再使用wx.saveImageToPhotosAlbum保存图片。
// shared_store/pages/collectionCode/collectionCode.js
const app = getApp()
const util = require("../../../utils/util.js")
let http_host = app.globalData.http_host
Page({
/**
* 页面的初始数据
*/
data: {
http_host: http_host, //域名
theme: app.globalData.style_color,
qr_code_img: "",
shop_name:"",
imagePath:"",
can_width:0,
can_height:0,
url1:"../../../pages/static/check.png",
urls1:http_host + "/shared_store/web/static/images/check.png",//线上图片地址
url3:"../../../pages/static/wechat_pay.png",
urls3:http_host + "/shared_store/web/static/images/wechat_pay.png",//线上图片地址
upload_url:"",
upload_url3:"",
upload_url1:"",
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
var that = this;
const new_id = wx.getStorageSync("store_id");
if (new_id) {
that.setData({
store_id: new_id,
});
}
that.get_pay_qr_code();
that.get_pay_qr_store();
that.unloadPic(that.data.urls3,3);
that.unloadPic(that.data.urls1,1);
},
/**
* 获取门店买单页二维码
* @method get_pay_qr_code
*/
get_pay_qr_code() {
var that = this;
util.requestData({
url: "/shared_store/web/index.php?m=pay&a=get_pay_qr",
method: "POST",
data: {
store_id: that.data.store_id,
},
success: function (res) {
if (res.errcode == 0) {
that.setData({
qr_code_img: res.data,
});
that.unloadPic(res.data,2);
setTimeout(() => {
that.makeImage();
}, 500);
} else {
wx.showToast({
title: res.errmsg,
icon: "none",
duration: 2000,
});
}
},
});
},
/**
* 获取买单页二维码(带绑定关系)
* @method get_pay_qr_store
*/
get_pay_qr_store() {
var that = this;
util.requestData({
url: "/shared_store/web/index.php?m=store&a=get_pay_qr",
method: "POST",
data: {
store_id: that.data.store_id,
},
success: function (res) {
if (res.errcode == 0) {
that.setData({
shop_name: res.data.shop_name,
});
} else {
wx.showToast({
title: res.errmsg,
icon: "none",
duration: 2000,
});
}
},
});
},
makeImage() {
var that = this;
const query = wx.createSelectorQuery();
query.select('#shareView').boundingClientRect();
query.select('#shareView').fields({ computedStyle: ['background-color', 'color', 'font-size', 'text-align', 'line-height'] }, function (res) {
console.log(res,'mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm'); // 查看获取的样式信息
});
query.exec((res) => {
if (res[0]) {
const dom = res[0];
const styles = res[1];
that.setData({
can_width:dom.width,
can_height:dom.height+66,
})
// 创建 canvas context
const canvasContext = wx.createCanvasContext('my-canvas');
// 设置背景颜色
canvasContext.setFillStyle("#112E98");
canvasContext.fillRect(0, 0, dom.width, dom.height+66);
// canvasContext.drawImage(that.data.url1, (dom.width-270) / 2, 60, 270, 39);//本地图片地址
canvasContext.drawImage(that.data.upload_url1, (dom.width-270) / 2, 60, 270, 39);//线上图片地址
// 设置文字样式
canvasContext.setFontSize(parseInt(styles['font-size']));
canvasContext.setTextAlign("center");
canvasContext.setFillStyle("#fff");
canvasContext.fillText('支持使用微信到店扫码买单', dom.width / 2, 140);
// 绘制一个圆角矩形作为背景
that.drawRoundedRect(canvasContext,20, 180, dom.width-40, 439, 12, '#fff');
canvasContext.drawImage(that.data.upload_url, (dom.width-208) / 2, 230, 208, 208);
// 设置文字样式
// 分割文本行
const canvasWidth = 275; // 根据实际情况设置
// 文本样式
const fontSize = 18;
const lineHeight = 25;
const lines = that.breakText(canvasContext, that.data.shop_name, canvasWidth, fontSize);
console.log(lines,'lines');
// 绘制文本行
lines.forEach((line, index) => {
canvasContext.font = 'bold 18px sans-serif'; // 加粗字体,18像素大小,sans-serif 字体
canvasContext.setTextAlign("center");
canvasContext.setFillStyle("#112E98");
canvasContext.fillText(line, dom.width / 2, (((index + 1) * lineHeight)+460)); // 设置文本位置,10为左边距
});
// canvasContext.drawImage(that.data.url3, (dom.width-110) / 2, 540, 110, 35);//本地图片地址
canvasContext.drawImage(that.data.upload_url3, (dom.width-110) / 2, 540, 110, 35);//线上图片地址
// 绘制到 canvas 上
canvasContext.draw(false, () => {
wx.canvasToTempFilePath({
canvasId: 'my-canvas',
success: (res) => {
this.setData({
imagePath: res.tempFilePath
});
},
fail: (err) => {
console.error('Failed to save canvas as image:', err);
}
});
});
}
});
},
// 绘制圆角矩形的函数
drawRoundedRect(ctx,x, y, width, height, radius, fillColor) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.arc(x + width - radius, y + radius, radius, -Math.PI / 2, 0);
//使用ctx.arcTo这里安卓有右上角圆角可能有问题,ios则没有
// ctx.arcTo(x + width, y, x + width, y + radius, radius);
ctx.lineTo(x + width, y + height - radius);
ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2);
// ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
ctx.lineTo(x + radius, y + height);
ctx.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI);
// ctx.arcTo(x, y + height, x, y + height - radius, radius);
ctx.lineTo(x, y + radius);
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
// ctx.arcTo(x, y, x + radius, y, radius);
ctx.closePath();
// 设置填充颜色
ctx.setFillStyle(fillColor);
ctx.fill();
},
//线上图片地址下载到本地转换成临时文件路径
unloadPic(url,ind){
var that = this;
// 下载网络图片
wx.downloadFile({
url: url, // 线上图片地址
success: function(res) {
if (res.statusCode === 200) {
const tempFilePath = res.tempFilePath; // 临时文件路径
if(ind==3){
that.setData({
upload_url3:tempFilePath
});
}else if(ind==1){
that.setData({
upload_url1:tempFilePath
});
}else{
that.setData({
upload_url:tempFilePath
});
}
} else {
console.error('下载图片失败,状态码:', res.statusCode);
}
},
fail: function(err) {
console.error('下载图片失败:', err);
}
});
},
//换行显示处理事件
breakText: function(ctx, text, maxWidth, fontSize) {
let words = text.split('');//拆分成单个所有的
console.log(words,'words');
let lines = [];
let currentLine = '';
words.forEach(word => {
const testLine = currentLine + word;
const metrics = ctx.measureText(testLine);//测量文本尺寸信息。目前仅返回文本宽度
const testWidth = metrics.width;
console.log(metrics,'metrics');
if (testWidth > maxWidth && currentLine.length > 0) {
lines.push(currentLine);
currentLine = word;
} else {
currentLine = testLine;
}
});
lines.push(currentLine);
return lines;
},
//保存图片到相册,提示保存成功
saveFun() {
const that = this;
// 请求用户授权保存图片到相册
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() {
// 用户授权成功,执行保存图片操作
wx.getImageInfo({
src: that.data.imagePath,
success(res) {
// res.path 是临时文件路径,可用于保存图片
wx.saveImageToPhotosAlbum({
filePath: res.path,
success() {
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
},
fail(err) {
wx.showToast({
title: '保存失败',
icon: 'none',
duration: 2000
});
console.error('保存失败', err);
}
});
},
fail(err) {
console.error('获取图片信息失败', err);
}
});
},
fail() {
// 用户拒绝授权,可以给出提示或再次请求授权
console.warn('用户拒绝授权');
}
});
return
//下载的话-本地图片临时文件路径这边安卓有的是不行的
wx.downloadFile({
url: that.data.imagePath,
success: function (res) {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
wx.showToast({
title: "保存成功",
icon: "success",
duration: 1000,
});
},
fail: function (err) {
console.log("保存失败", err);
wx.showToast({
title: "取消保存",
icon: "none",
duration: 2000,
});
},
});
},
fail: function (err) {
console.log("保存失败==", err);
wx.showToast({
title: "取消保存",
icon: "none",
duration: 2000,
});
},
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {},
/**
* 生命周期函数--监听页面显示
*/
onShow() {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {},
});