一、需求
本次【世纪盛典】活动,分为三期,每期都有海报相关的工作内容。
海报带有动态的个人信息、头像、证书编号及二维码等。
本次活动需要在微信h5、小程序、app里面正常启动。
二、经过
网上查找使用了 html2canvas 这个插件来生成海报。
首先,加载依赖包
npm install html2canvas --save
使用
import html2canvas from 'html2canvas'
html2canvas 是基于已有 DOM 进行截图生成图片的,已有 DOM 如下:
<div @click="onShowBookClick">查看证书</div>
<!-- 证书 -->
<div class="book">
<div class="body" ref="imageWrapper">
<img id="img" src="../../assets/imgs/***.png" alt="" />
<div class="close" @click="onHidePosterClick"></div>
<div class="num">证书编号:{{poster.certificateNumber}}</div>
<div class="name">{{poster.donatePeople}}</div>
<div class="text">
感谢您参与感谢您参与感谢您参与感谢您参与感谢您参与感谢您参与感谢您参与感谢您参与捐赠<span class="red">{{poster.donateSum}}</span>元,捐款将全部用于************。
<div class="thanks">特发此证,谨致谢忱。</div>
</div>
<div class="right_text">
*********公益基金会<br />
{{poster.createTime}}
</div>
<div class="generate">
<img src="../../assets/imgs/***.png" />
</div>
</div>
</div>
// 只提供了html2canvas截图的功能,具体在生成海报前的动态赋值,加载toast这里就不提供了
onShowBookClick() {
html2canvas(this.$refs.imageWrapper).then(canvas => {
// 生成图片
const dataURL = canvas.toDataURL("image/png")
_this.poster.url = dataURL
_this.poster.isVisible = true
})
}
生成的 poster.url 可以放在需要显示的 DOM 节点上,通过 poster.isVisible 控制海报的DOM节点是否要显示。
测试结果
安卓上面没问题,IOS 的不能显示,console 里面也报错了。
经调查,canvas.toDataURL("image/png") 不能兼容 IOS
三、最后
用了 Canvas2Image 插件,把绘制好的 canvas 转成图片
晚上下载了 Canvas2Image.js 插件,然后又稍微整理成 vue 可以 import 的
// Canvas2Image.js
let Canvas2Image = function() { // canvas2image.js code }();
export default Canvas2Image
// 二次修改生成海报页面
onShowBookClick() {
html2canvas(this.$refs.imageWrapper, {
useCORS: true,
allowTaint: true, // 允许跨域图片
}).then(canvas => {
// 生成图片
const wid = canvas.width
const hei = canvas.height
const img = Canvas2Image.convertToJPEG(canvas, wid, hei)
const dataURL = img.getAttribute('src')
_this.poster.url = dataURL
_this.poster.isVisible = true
})
}
四、发现问题
问题一、生成的海报,部分手机图片下面有大约10像素的空白
解决: .body{ font-size: 0; }
问题二、要截屏的DOM刚开始是固定放在可视区,
position: fixed; left: 0; top: 0;z-index: -9,
这种情况下 IOS 机型随意上下拖动手机就可以看到DOM元素
解决:修改样式,不需要截屏是,top: -3000px; 需要截屏时DOM的top回归0。
截屏时保证DOM在可视区即可。
问题三、生成海报后,IOS长按图片会有选中情况
解决:写一个统一的样式,哪块不需要选中,调用一下即可。
touch-none-select() {
-webkit-touch-callout none
-webkit-user-select:none; /*webkit浏览器*/
-khtml-user-select:none; /*早期浏览器*/
-moz-user-select:none;/*火狐*/
-ms-user-select:none; /*IE10*/
user-select:none;
}
问题四、目前的海报不清晰,看着头晕,看看能不能调整调整。
解决:生成canvas时,把canvas放大几倍,显示海报时,正常比例显示即可。
// 把生成海报的功能单独在methods里面提出来
doCanvasAndImg() {
const _this = this
const downloadContent = this.$refs.imageWrapper
const width = downloadContent.offsetWidth
const height = downloadContent.offsetHeight
const canvas = document.createElement('canvas')
const scale = 2
canvas.width = width * scale
canvas.height = height * scale
// 放大后再缩小提高清晰度
canvas.getContext('2d').scale(scale, scale)
html2canvas(downloadContent, {
useCORS: true, // PS:跨域问题仅前端设置是不行的,需要后台也放开
allowTaint: true, // 允许跨域图片
width: downloadContent.offsetWidth, // 因为多出的需要剪裁掉,
height: downloadContent.offsetHeight,
}).then(canvas => {
// 生成图片
const context = canvas.getContext('2d')
// 【重要】关闭抗锯齿
context.mozImageSmoothingEnabled = false
context.webkitImageSmoothingEnabled = false
context.msImageSmoothingEnabled = false
context.imageSmoothingEnabled = false
const wid = canvas.width
const hei = canvas.height
// 【重要】默认转化的格式为png,也可设置为其他格式
const img = Canvas2Image.convertToJPEG(canvas, wid, hei)
const dataURL = img.getAttribute('src')
_this.poster.url = dataURL
_this.$refs.toast.hide()
_this.poster.isVisible = true
_this.noScroll() // 该方法是
_this.poster.isClick = false
})
},
问题五、昵称过长,导致海报的布局错乱。PS:昵称遮盖了正文。
解决:确定昵称显示的宽度,动态计算一个文字占用多少空间。
fittingString(str, maxWidth) {
const span = document.createElement('span')
// 以下样式设置,是同步海报--昵称的样式,便于正确计算昵称的宽度
span.style.visibility = 'hidden'
span.style.fontSize = '28px'
span.style.fontWeight = '500'
span.style.letterSpacing = '3px'
span.style.fontFamily = 'PingFangSC-Medium,PingFang SC'
span.style.display = 'inline-block'
document.body.appendChild(span)
span.textContent = str
let width = span.offsetWidth
const ellipsis = '…'
// var ellipsisWidth = c.measureText(ellipsis).width
if (width <= maxWidth) {
return str
} else {
let len = str.length
while (width >= maxWidth && len-- > 0) {
str = str.substring(0, len)
span.textContent = str
width = span.offsetWidth
}
return str + ellipsis
}
},
// 调用
this.fittingString('昵称**************name*****', 340)
问题六:证书偶先部分空白
解决:后来发现都是页面滚动后,滑动多少区域,证书就有多少区域空白;
使用 html2canvas 必须保证要截图的区域在页面可视区
scrollTop() {
window.pageYOffset = 0
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
document.getElementById('prize').scrollTop = 0
},
doCanvasAndImg() {
// 使用时
if (this.poster.qrUrl) { // 二维码存在,再绘制图片
this.scrollTop()
setTimeout(() => {
_this.doCanvasAndImg()
}, 1000)
}
问题七、部分手机海报的最下边出现1px的白色线条
解决:
html2canvas(downloadContent, {
useCORS: true,
allowTaint: true, // 允许跨域图片
backgroundColor: null, // 解决生成的图片部分手机有白边问题
width: downloadContent.offsetWidth, // 因为多出的需要剪裁掉,
height: downloadContent.offsetHeight,
}).then(canvas => {
// 生成图片