前言
本人小白,文章只是为了记录我遇到的一些难点,解决思路肯定不是很完美,欢迎大佬批评指正。文章最后会说说我的解决过程。
一、功能知识点提前学习
1.第三方库
- uniapp-qrcode
import wxcode from 'uniapp-qrcode'; // 引入插件(没有安装的自己安装一下啊)
// 使用此方法便可以生成一个二维码
wxcode.qrcode('qrcodeNo', 'http://www.baidu.com', 445, 445);
// 第一个'qrcodeNo'是你要添加二维码的画布的canvas-id(最好也给个id与其同名)
// 第二个'http://www.baidu.com'扫码后跳转的链接
// 第三个 445 二维码的宽 单位px
// 第四个 445 二维码的高 单位px
2.uniapp的API
uni.canvasToTempFilePath是 uniapp 中用于将<canvas>画布内容保存为临时文件路径的 API。该 API 允许将绘制在<canvas>上的内容转换为图片,并将图片保存为临时文件,通常用于分享、保存或展示图片等功能。
-
作用: 将
<canvas>画布的内容转换为图片,并保存为临时文件路径。 -
参数:
-
options:一个对象,包含以下属性:canvasId:(String) 必填,需要指定<canvas>的 id 属性值,表示要将哪个画布的内容保存为图片。x:(Number) 可选,表示画布内容左上角在目标图片中的横坐标,默认为 0。y:(Number) 可选,表示画布内容左上角在目标图片中的纵坐标,默认为 0。width:(Number) 可选,表示要绘制的画布内容的宽度,默认为画布本身的宽度。height:(Number) 可选,表示要绘制的画布内容的高度,默认为画布本身的高度。destWidth:(Number) 可选,表示目标图片的宽度,默认为画布本身的宽度。destHeight:(Number) 可选,表示目标图片的高度,默认为画布本身的高度。fileType:(String) 可选,目标文件的类型,默认为 'png',可选值为 'jpg'、'png'。
-
-
返回值: 无返回值,会触发 success 或 fail 回调函数。
-
示例:
uni.canvasToTempFilePath({
canvasId: 'qrcodeNo',
success: (res) => { // 成功回调
// 在H5平台下,tempFilePath 为 base64
console.log('55555', res.tempFilePath)
},
})
uni.createCanvasContext是 uniapp 中用于创建一个 Canvas 2D 上下文对象的 API。通过这个上下文对象,你可以在<canvas>画布上进行绘制操作,包括绘制图像、文字、形状等。 详解如下:
-
作用: 创建一个 Canvas 2D 上下文对象,用于绘制
<canvas>画布上的内容。 -
语法:
uni.createCanvasContext(canvasId, this) -
参数:
canvasId:(String) 必填,表示要获取哪个<canvas>画布的上下文对象,需要传入该画布的 id(canvas-id) 属性值。this:(Object) 可选,表示上下文对象的作用域,通常传入 this 即可。
-
返回值: 返回一个 Canvas 2D 上下文对象,通过这个对象你可以进行绘制操作
-
示例:
const ctx = uni.createCanvasContext('myCanvasId', this);
保存图片到系统相册。
参数说明:
filePath(必填):要保存的图片文件的本地临时路径,需要是临时文件路径,通常是通过其他 API(如uni.chooseImage)获取到的图片路径。
回调函数:
-
success:保存图片成功时触发的回调函数,不传或传null。 -
fail:保存图片失败时触发的回调函数,返回一个错误对象,包含错误信息。 -
complete:不论保存图片成功或失败,都会触发的回调函数。 -
uni.createSelectorQuery()
这个api内容有点多,总的来说就是获取节点信息,有点像document然后根据id或类名等获取元素的一些信息的
二、代码讲解
1.html部分
<template>
<view class="content">
<view class="tu">
<!-- 用于生成二维码的画布 在二号画布获取生成的二维码后后会被v-if卸载掉 -->
<canvas canvas-id="qrcodeNo" id="qrcodeNo" style="height: 500rpx;width: 500rpx;" v-if="isShow"></canvas>
<!-- 真正用于展示 和 截图的画布 -->
<canvas class="er_wei" canvas-id="qrcodeId" id="qrcodeId" style="width: 654rpx; height: 900rpx;"></canvas>
</view>
<!-- 下载按钮 -->
<button class="btn" @click="saveElementContent">保存</button>
</view>
</template>
2.js部分
1. 在一号画布(qrcodeNo)上用插件生成二维码
import wxcode from 'uniapp-qrcode'; // 引入插件(没有安装的自己安装一下啊)
mounted() { // 注意生命周期
// 这个插件用法很简单上面介绍了
wxcode.qrcode('qrcodeNo', 'http://www.baidu.com', 445, 445);
},
2.将一号画布(qrcodeNo)的二维码保存为临时路径
mounted() {
uni.canvasToTempFilePath({ // 保存canvas为图片
canvasId: 'qrcodeNo', // canvas的ID
quality: 1, // 生成的图片质量 0.1-1
success(res) { // 成功回调 res.tempFilePath就是临时路径
console.log('我就是临时路径',res.tempFilePath);
}
})
},
3.有了临时路径就把二维码和背景图画到二号画布(qrcodeId)中
// 这样分开写CV有点乱 但是方便学习
mounted() {
const ctx = uni.createCanvasContext('qrcodeId', that); // 获二号画布上下文对象
ctx.drawImage('/static/img/erweima.png', 0, 0, imageWidth, imageHeight); //背景图画进去
ctx.drawImage(res.tempFilePath, codeX, codeY, codeSize, codeSize); // 从一号画布获取的二维码也画进去
ctx.draw() // 我也是第一次用canvas 看视频学习的时候 不用调这个方法就可以绘画 uniapp一定要加 不然不画 我也不知道为啥
},
3.点击保存按钮时获取二号画布(qrcodeId)的大小(方便完整截取)
// 点击事件
saveElementContent() {
uni.createSelectorQuery()
.in(this)
.select('.er_wei') // 获取 .er_wei 也就是二号画板的节点
.boundingClientRect(rect => { // 对他的布局位置请求
const bgWidth = rect.width; // 背景图的宽度
const bgHeight = rect.height; // 背景图的高度
// 在这里执行截图操作,
// ..........
})
.exec();
},
4.将二号画布(qrcodeId)保存为临时路径并保存相册
uni.canvasToTempFilePath({ // 保存canvas为图片
canvasId: 'qrcodeId',
quality: 50,
x: 0,
y: 0,
width: bgWidth,
height: bgHeight,
success: (res) => {
uni.saveImageToPhotosAlbum({ // 保存图片到相册
filePath: res.tempFilePath, // 将二号画板截的图的临时路径作为参数
success: () => {
// 保存成功
}
})
},
})
三、完整代码(方便CV)
<template>
<view class="content">
<view>
<!-- 用于生成二维码的画布 在二号画布获取生成的二维码后后会被v-if卸载掉 -->
<canvas canvas-id="qrcodeNo" id="qrcodeNo" style="height: 500rpx;width: 500rpx;" v-if="isShow"></canvas>
<!-- 真正用于展示 和 截图的画布 -->
<canvas class="er_wei" canvas-id="qrcodeId" id="qrcodeId" style="width: 654rpx; height: 900rpx;"></canvas>
</view>
<!-- 下载按钮 -->
<button class="btn" @click="saveElementContent">保存</button>
</view>
</template>
<script>
import wxcode from 'uniapp-qrcode';
export default {
data() {
return {
isShow: true, // 控制一号画布是否隐藏
};
},
mounted() {
wxcode.qrcode('qrcodeNo', 'http://www.baidu.com', 445, 445);
const that = this
uni.canvasToTempFilePath({ // 保存canvas为图片
canvasId: 'qrcodeNo', // canvas的ID
quality: 1, // 生成的图片质量 0.1-1
success(res) { // 成功回调 res.tempFilePath就是临时路径
that.isShow = false // 获取一号画布的二维码 就卸载一号画布
const ctx = uni.createCanvasContext('qrcodeId', that); // 获二号画布上下文对象
const imageWidth = uni.upx2px(654); // 将px转为rpx
const imageHeight = uni.upx2px(900);
const codeSize = uni.upx2px(480);
const codeX = uni.upx2px(115);
const codeY = uni.upx2px(240);
ctx.drawImage('/static/img/erweima.png', 0, 0, imageWidth, imageHeight); //背景图画进去
ctx.drawImage(res.tempFilePath, codeX, codeY, codeSize, codeSize); // 从一号画布获取的二维码也画进去
ctx.draw() // 我也是第一次用canvas 看视频学习的时候 不用调这个方法就可以绘画 uniapp一定要加 不然不画 我也不知道为啥
}
})
},
methods: {
// 点击保存
saveElementContent() {
uni.createSelectorQuery()
.in(this)
.select('.er_wei') // 获取 .er_wei 也就是二号画板的节点
.boundingClientRect(rect => { // 对他的布局位置请求
const bgWidth = rect.width; // 背景图的宽度
const bgHeight = rect.height; // 背景图的高度
// 在这里执行截图操作,
uni.canvasToTempFilePath({ // 保存canvas为图片
canvasId: 'qrcodeId',
quality: 50,
x: 0,
y: 0,
width: bgWidth,
height: bgHeight,
success: (res) => {
uni.saveImageToPhotosAlbum({ // 保存图片到相册
filePath: res.tempFilePath, // 将二号画板截的图的临时路径作为参数
success: () => {
uni.showToast({
title: '保存成功'
})
}
})
},
fail: (err) => {
uni.showToast({
title: '保存失败'
})
}
})
})
.exec();
},
},
};
</script>
<style lang="scss" scoped>
.content {
@include flex(true);
.er_wei {
margin-top: 100rpx;
}
.btn {
width: 580rpx;
height: 80rpx;
background: #327FFF;
border-radius: 200rpx;
font-size: 30rpx;
color: #FFF;
margin-top: 100rpx;
}
}
</style>
四、解决过程
先说说我为什么这么做吧
因为我之前在h5做过这种功能,我一开始觉得我一会都写完了,不就找一个二维码插件嘛,
我就找了个简单的uniapp-qrcode,然后把这个生成二维码的画布定位到背景图上,然后再我使用html2canvas截取DOM元素,谁知他居然报错了,说什么没有document。
我多方询问截图插件,终于得知uniapp有原生的的保存图片,我苦心钻研终于看懂文档,得知只能截取画布不能截取DOM时,我反手就把背景入了画布,谁知加入了背景二维码却被挤掉了,在我刑讯逼供下GPT告诉了我原因,但它满嘴胡话,一会这个原因,一会那个原因,最终无法解决。
然后我又多次尝试其他二维码插件 qrcode、vue-qrcode、等等等等,结果都不尽人意,要么就是报错,要么就是浏览器显示,手机上据不显示。
山重水复疑无路,柳暗花明又一村。我蒙幸得到某位高人抬爱,我问高人,以我现在的进度有没有什么强行破解之法,高人听后,就告诉我这个方法。