最近做了一个生成海报功能,用户上传图片之后自动生成一张海报,由于海报样式很复杂,使用canvas绘制效率太低了,因此我想用html+css排版,然后将dom转成canvas,所以我找到了一个插件html2canvas
,这个插件帮助我们将html转成canvas,不需要我们花时间去绘制了,这样生成的canvas可以转成base64并上传到七牛上面。
html2canvas安装
// Install NPM
npm install --save html2canvas
// Install Yarn
yarn add html2canvas
html2canvas使用
我们先将html写出来,并给要转换的dom父元素一个id=htmlContent
<template>
<div>
<!-- 需要转canvas的区域 -->
<div id="htmlContent" style="padding: 10px; background: #f5da55">
<h4 style="color: #000; ">Hello world!</h4>
</div>
<!-- end -->
<div>
<button @click='html2canvas'>生成canvas</button>
</div>
</div>
</template>
接着写一个html2canvas方法并添加配置,.then里面返回的就是生成的canvas,注意这里html2canvas使用的是Promise语法,更多配置项可以自行查找。
<script>
export default {
methods: {
html2canvas () {
let that = this
let opts = {
logging: true, // 启用日志记录以进行调试 (发现加上对去白边有帮助)
allowTaint: true, // 否允许跨源图像污染画布
backgroundColor: null, // 解决生成的图片有白边
useCORS: true // 如果截图的内容里有图片,解决文件跨域问题
}
html2canvas(document.querySelector('#htmlContent'), opts).then((canvas) => {
let url = canvas.toDataURL('image/png')
that.dataURL = url.split(';base64,')[1]
document.querySelector('#img').src = url
})
},
}
}
</script>
拿到canvas之后需要转换成base64上传到七牛上面,所以这里使用到了canvas的toDataURL方法去获取base64字符串,注意这里拿到的是';base64,'+字符串,所以需要分割split只要字符串
canvas.toDataURL(_type_, _encoderOptions_)
* `type`可选
图片格式,默认为 `image/png`
* `encoderOptions`可选
在指定图片格式为 `image/jpeg 或` `image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量`。如果超出取值范围,将会使用默认值 `0.92`。其他参数会被忽略。
ios13.4.1中失效的问题
在测试中遇到html2canvas的坑,在ios13.4.1版本中部分手机失效,去github上查询发现是html2canvas@1.0.0-rc.5
版本包有问题,将html2canvas回退版本到1.0.0-rc.4就好了
github issue
注意html2canvas不支持css的伪类
上传七牛云
拿到base64了需要上传到七牛云上,七牛文档提供了这个接口
下面是七牛提供的demo,注意Authorization
的值 UpToken 获取的token
,UpToken 和token之间有一个空格
function putb64(){
var pic = "填写你的base64后的字符串";
var url = "http://upload.qiniup.com/putb64/20264"; //非华东空间需要根据注意事项 1 修改上传域名
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if (xhr.readyState==4){
document.getElementById("myDiv").innerHTML=xhr.responseText;
}
}
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.setRequestHeader("Authorization", "UpToken 填写你从服务端获取的上传token");
xhr.send(pic);
}
我项目里使用的axios,所以也将axios用法po出来
在上传之前先请求后台接口拿到token,所以这里用到了async/await
,然后设置headers,七牛的域名拼接上res.key'https://xxx.com/' + res.key
就是图片的地址啦。
async putb64 () {
await this.getToken()
let that = this
let pic = this.dataURL
let url = this.up_host + '/putb64/-1'
http.post(url, pic, {
headers: {
'Content-Type': 'application/octet-stream',
'Authorization': 'UpToken ' + this.token
}
})
.then(res => {
that.screenImgUrl = 'https://xxx.com/' + res.key + '?imageView2/0/w/1280/h/720'
that.$emit('handleGetUrl', this.screenImgUrl)
})
},
getToken () {
let params = {
bucket: 'bucket',
file_name: 'test/' + new Date().getTime() + '.png'
}
return new Promise((resolve, reject) => {
http.get('/qn/sign/v3', { params: params })
.then(res => {
this.token = res.token
this.up_host = res.up_host
resolve()
})
})
}
wx.chooseImage
由于是微信h5,所以上传图片用到了微信的jssdk --- wx.chooseImage
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
}
});
注意:res.localIds是一个数组,数组长度等于你指定的count的值,也就是当count为1时,你想拿到src需要通过res.localIds[0]
localId可以作为img标签的src属性显示图片,所以上传后的图片只用localId显示就可以了,
但是这里就有坑了,因为部分安卓手机生成canvas时出现 Error loading image weixin://resourceid/xxx
的报错,这个兼容问题需要用到getLocalImgData
找到localID的图片转换成base64就可以解决了
wx.getLocalImgData({
localId: localId,
success: function (res) {
var localData = res.localData
if (localData.indexOf('data:image') !== 0) {
// 判断是否有这样的头部
localData = 'data:image/jpeg;base64,' + localData
}
// 第一个替换的是换行符,第二个替换的是图片类型,因为在IOS机上测试时看到它的图片类型时jgp,
// 这不知道是什么格式的图片,为了兼容其他设备就把它转为jpeg
localData = localData.replace(/\r|\n/g, '').replace('data:image/jgp', 'data:image/jpeg')
callback && callback(localData)
},
fail: function (res) {
console.log(res)
}
})
})
完整代码
<template>
<div>
<!-- 需要转canvas的区域 -->
<div id="htmlContent" style="padding: 10px; background: #f5da55">
<h4 style="color: #000; ">Hello world!</h4>
<img src="uploadImg">
</div>
<!-- end -->
<div>
<button @click='handleUploadImg'>上传图片</button>
</div>
</div>
</template>
<script>
import html2canvas from 'html2canvas'
export default {
data () {
return {
dataURL: '',
up_host: '',
token: '',
uploadImg: ''
}
},
methods: {
handleUploadImg () {
let that = this
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
res.localIds.forEach(item => {
wx.getLocalImgData({
localId: localId,
success: function (res) {
var localData = res.localData
if (localData.indexOf('data:image') !== 0) {
// 判断是否有这样的头部
localData = 'data:image/jpeg;base64,' + localData
}
// 第一个替换的是换行符,第二个替换的是图片类型,因为在IOS机上测试时看到它的图片类型时jgp,
// 这不知道是什么格式的图片,为了兼容其他设备就把它转为jpeg
localData = localData.replace(/\r|\n/g, '').replace('data:image/jgp', 'data:image/jpeg')
that.uploadImg = localData
that.html2canvas()
},
fail: function (res) {
console.log(res)
}
})
})
}
});
},
html2canvas () {
let that = this
let opts = {
logging: true, // 启用日志记录以进行调试 (发现加上对去白边有帮助)
allowTaint: true, // 否允许跨源图像污染画布
backgroundColor: null, // 解决生成的图片有白边
useCORS: true // 如果截图的内容里有图片,解决文件跨域问题
}
html2canvas(document.querySelector('#htmlContent'), opts).then((canvas) => {
let url = canvas.toDataURL('image/png')
that.dataURL = url.split(';base64,')[1]
document.querySelector('#img').src = url
that.putb64()
})
},
async putb64 () {
await this.getToken()
let pic = this.dataURL
let url = this.up_host + '/putb64/-1'
http.post(url, pic, {
headers: {
'Content-Type': 'application/octet-stream',
'Authorization': 'UpToken ' + this.token
}
})
.then(res => {
this.screenImgUrl = 'https://xxx.com/' + res.key + '?imageView2/0/w/1280/h/720'
this.$emit('handleGetUrl', this.screenImgUrl)
})
},
getToken () {
let params = {
bucket: 'bucket',
file_name: 'test/' + new Date().getTime() + '.png'
}
return new Promise((resolve, reject) => {
http.get('/qn/sign/v3', { params: params })
.then(res => {
this.token = res.token
this.up_host = res.up_host
resolve()
})
})
}
}
}
</script>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。