一次刷脸测年终奖的踩坑之旅
关键词:图片上传、生成海报
需求
上一年的财神节活动,我们的品宣爸爸说要搞事情!!!做一个刷脸测年终奖的H5~爸爸的需求,大家都懂得...但这是目前唯一一个让我感觉稍微有点意思的活动了~
先来看看我们最后的结果页长啥样:
- 左图为开始页面,包含上传按钮;
- 右图为结果页,也就是包含我们最终生成的海报部分,但由于是上年的活动了,所以接口已经废弃了,无法获取成功的结果。
看完这货,我们来整理下需求:
- 图片上传:需同时支持本地上传和拍照上传
- 图片预览与编辑:支持图片预览、缩放、移动、裁剪和压缩
- 生成海报:根据上传的图片和测试结果生成一张海报
实现方案
<input type="file"/>
实现图片上传FileReader
实现预览,canvas
编辑图片html2canvas
实现 DOM 转为 canvascanvas
生成最终海报
采坑表演
1、 <input type="file"/>
在iOS上可以成功调起拍照和图库,但在Android上只能调起图库而没有拍照功能,后来查了一下API,发现加上accept="image/*"
属性就可以解决,done.
2、 开始按钮在UC浏览器竟然消失不见了~~这种情况真的一点也不意外,没有把我们整个页面“吃掉”已经很客气了,但UC还是我们兼容性要求范围内,所以只好乖乖debugger。经过一番排除,发现是UC的广告模式把我们的按钮当作广告处理了,按钮用了一个bottom
的class, 换之,Done。
3、 iOS拍照上传后,出现了图片被旋转了的情况。引入Exif-js,Exif.js是一个可以读取图像的原始数据的Javascript库,例如:拍照方向、相机设备型号、拍摄时间、ISO 感光度、GPS 地理位置等等。
这里我们主要针对拍照方向作相应的处理:
// Exif获取照片的拍照方向
let direction
Exif.getData(file, function() {
direction = Exif.getTag(this, 'Orientation')
})
// 判断图片方向,重置canvas大小,确定旋转角度,iphone默认的是home键在右方的横屏拍摄方式
switch (direction) {
// iphone横屏拍摄,此时home键在左侧
case 3:
degree = 180
break
// iphone竖屏拍摄,此时home键在下方(正常拿手机的方向)
case 6:
degree = 90
break
// iphone竖屏拍摄,此时home键在上方
case 8:
degree = 270
break
}
// 使用canvas旋转校正
let canvas = document.createElement('canvas')
let context = canvas.getContext('2d')
context.rotate(degree * Math.PI / 180)
4、 最后生成的海报,在手机端发现模糊感明显。
在开发时,用chrome模拟手机环境时,并没有出现这种情况。既然用的是html2canvas这个库,那就翻下文档找找有没有相关的资料。
首先,html2canvas的官方文档是这样介绍的:
The script traverses through the DOM of the page it is loaded on. It gathers information on all the elements there, which it then uses to build a representation of the page. In other words, it does not actually take a screenshot of the page, but builds a representation of it based on the properties it reads from the DOM.
简单来说就是,这个库不是真的对页面进行截图,而且基于从DOM元素读取的属性然后绘制canvas。
这么一看,然后就想到了可能是移动端的像素密度计算的问题~
设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系
设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向
然而文档并没后有可以配置像素比的地方啊~~~(新版本已经支持了,生不逢时啊...)那我们只能另辟蹊径了。
继续寻找突破点,功夫不负有心人,发现可以传入自定义canvas作为配置项,而我的处理方案是:获取DPR -> 根据比例创建更大的canvas -> 传入canvas进行绘制
/**
* 根据window.devicePixelRatio获取像素比
*/
function getDPR() {
if (window.devicePixelRatio && window.devicePixelRatio > 1) {
return window.devicePixelRatio
}
return 1
}
// 获取想要转换的 DOM 样式
const box = window.getComputedStyle(dom)
// DOM 节点计算后宽高
const dWidth = parseInt(box.width, 10)
const dHeight = parseInt(box.height, 10)
// 获取像素比
const dpr = getDPR()
// 创建自定义 canvas 元素
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
// 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比
canvas.width = dWidth * dpr
canvas.height = dHeight * dpr
// 设定 canvas css宽高为 DOM 节点宽高
canvas.style.width = `${dWidth}px`
canvas.style.height = `${dHeight}px`
// 将所有绘制内容放大像素比倍
context.scale(dpr, dpr)
// 将自定义 canvas 作为配置项传入,开始绘制
html2canvas(dom, {canvas, useCORS: true})
交付
经过N次修改和一顿的采坑,终于在痛苦中交付~
刷脸测年终奖,可点击左边链接或扫描下面二维码体验。
最后,希望以上分享能够对大家以后的开发有所帮助,谢谢~