需求描述
在一个页面中底部有个保存图片的功能,点击能够保存一张生成的自定义表格图片。
第一眼见到这个需求 自己会出现了两个问题
- 如何去处理图片中的自定义内容以及样式
- 如何将自定义内容转化成图片
至于保存图片,uniapp有对应的api去实现uni.saveImageToPhotosAlbum,不做赘述
实现效果展示
下面就是生成的图片
实现步骤
大致划分成以下几个步骤
1、需要在目标页面画出图片内容的html代码,并置于于屏幕可视区域外形成隐藏的效果(不是display:none隐藏元素)
2、需要将html转成canvas的形式,再通过toDataURL方法转成base64图片形式
3、将base64转换成临时图片路径,即可展示出,并下载
已知需要用到两个插件来帮助实现
一个是html转canvas的插件 html2canvas可通过npm下载
npm install html2canvas -D
一个是将base64图片格式转本地地址的Dcloud插件image-tools
1、画需要生成图片的元素
即就是生成图片的样式 还是需要我们自己来搞出来。可以封装成一个组件形式,这里就不贴代码,因为这不是核心逻辑,况且大家需要生成图片的样式都不一样,唯一需要注意的是,这块元素是要脱离文档流,并置于屏幕外,不让用户看到
将生成图片的代码封装成一个组件,如下 需要声明一个id 为后续转canvas做准备
<KitcalculatorTable id="pagePoster"/>
并将这块区域通过固定定位置于可视区外,这样就看不到了,不能用v-if或者v-show、display:none去隐藏,这样在后续html转canvas是失败的,无法获取到该区域元素
position: fixed;
top: -999999rpx;
background-color: #fff;
padding: 30rpx;
2将html转成canvas的形式,再转成base64图片形式
<template>
<view class="html2Canvas" id="pagePoster">
<view class="table">
<view class="kitCalculatorName">{{ pageData.kitCalculatorName }}</view>
<KitcalculatorTable :tableData="tableData" :isSumData="true" />
<view class="average-data">
<view style="margin-right: 50rpx">件套平均价格:{{ pageData.averagePriceOneDesc }}</view>
<view v-if="visiblePrice2">平均价格2:{{ pageData.averagePriceTwoDesc }}</view>
</view>
</view>
<view class="bottom-logo">
<img class="code" :src="codeImage" />
<view>
<view class="">报盘计算器,长按识别二维码使用</view>
<img class="logo" :src="logoImage" />
</view>
</view>
</view>
<view class="muji-button" style="margin: 0 24rpx" @click="canvasImage.generateImageSave">保存图片</view>
<view class="muji-button" @click="canvasImage.generateImageShare">分享</view>
</template>
<script lang="renderjs" module="canvasImage">
import html2canvas from 'html2canvas'
export default {
mounted() {},
methods: {
generateImageSave(){
this.generateImage('save')
},
generateImageShare(){
this.generateImage('share')
},
// 生成图片需要调用的方法
generateImage(methodType) {
this.$ownerInstance.callMethod('openLoading', '正在生成图片~')
setTimeout(() => {
const dom = document.getElementById('pagePoster') // 需要生成图片内容的 dom 节点
html2canvas(dom, {
width: dom.clientWidth, //dom 原始宽度
height: dom.clientHeight,
scrollY: 0, // html2canvas默认绘制视图内的页面,需要把scrollY,scrollX设置为0
scrollX: 0,
useCORS: true, //支持跨域
// scale: 2, // 设置生成图片的像素比例,默认是1,如果生成的图片模糊的话可以开启该配置项
}).then((canvas) => {
// 生成成功
// html2canvas 生成成功的图片链接需要转成 base64位的url
this.$ownerInstance.callMethod('receiveRenderData',canvas.toDataURL('image/png'))
}).catch(err=>{
this.$ownerInstance.callMethod('_errAlert',`【生成图片失败,请重试】${err}`)
})
}, 300)
},
}
}
</script>
<script>
import { pathToBase64,base64ToPath} from 'image-tools';
export default {
props: {
pageData: {
type: Object,
default: () => {
return {};
},
},
},
data() {
return {
temUrl: '', //生成的临时路径图片
codeImage: '', // 件套二维码图片
logoImage: '', // logo图片
};
},
created() {
// 处理静态图片 转为base64
this.actionImage();
},
methods: {
// 处理已知静态图片 转base64格式
actionImage() {
this.turnBase64Image(require('@/static/logo/down-code.png'), 'codeImage');
this.turnBase64Image(require('@/static/logo/muji-logo.png'), 'logoImage');
},
// 将图片转为base 64 位url
turnBase64Image(img, key) {
uni.getImageInfo({
src: img,
success: (image) => {
pathToBase64(image.path)
.then((base64) => {
this[key] = base64;
})
.catch((error) => {
console.log('转换失败:', error);
});
},
fail: (err) => {
console.log('将本地图片转为base 64报错:', err);
},
});
},
/* 将base64 位的图片路径转换为 临时路径 */
loadBase64Url(imageStr) {
const that = this;
base64ToPath(imageStr)
.then((path) => {
this.temUrl = path;
this.closeLoading()
this.saveImg()
})
.catch((error) => {
console.error('临时路径转换出错了:', error);
});
},
// 获取生成的base64 图片路径
receiveRenderData(val) {
const url = val.replace(/[\r\n]/g, ''); // 去除base64位中的空格
this.loadBase64Url(url);
},
// 报错alert
_errAlert(content) {
uni.showModal({
title: '提示',
content: content,
});
},
saveImg() {
uni.saveImageToPhotosAlbum({
filePath: this.temUrl,
success: function () {
uni.showToast('保存图片成功');
}
});
},
},
};
</script>
需要注意的两点是,这里要新建一个script节点,将语言改成renderjs的形式 在这之内做html转canvas的操作
再就是需要把生成图片中的已知静态图片 如下载二维码、企业logo转换成base64的形式
保存图片
最后就是点击保存后,调用canvasImage下的generateImage去生成图片,拿到base64格式图片通过image-tools的base64ToPath方法去转成临时路径再下载就ok了
题外话
这里涉及到renderjs的通讯知识,还是有点坑的
比如在renderjs模块调用script的方法是可以直接
this.$ownerInstance.callMethod('script内方法名',需要带的参数)
目前只了解可以在dom去调用renderjs方法,在script内貌似不能直接用。大家就要根据需求去灵活使用这个东西