一次刷脸测年终奖的踩坑之旅

关键词:图片上传、生成海报


需求

上一年的财神节活动,我们的品宣爸爸说要搞事情!!!做一个刷脸测年终奖的H5~爸爸的需求,大家都懂得...但这是目前唯一一个让我感觉稍微有点意思的活动了~

先来看看我们最后的结果页长啥样:

  • 左图为开始页面,包含上传按钮;
  • 右图为结果页,也就是包含我们最终生成的海报部分,但由于是上年的活动了,所以接口已经废弃了,无法获取成功的结果。

预览图

看完这货,我们来整理下需求

  • 图片上传:需同时支持本地上传和拍照上传
  • 图片预览与编辑:支持图片预览、缩放、移动、裁剪和压缩
  • 生成海报:根据上传的图片和测试结果生成一张海报

实现方案

  1. <input type="file"/>实现图片上传

  2. FileReader实现预览,canvas编辑图片

  3. html2canvas实现 DOM 转为 canvas

  4. canvas生成最终海报

采坑表演

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次修改和一顿的采坑,终于在痛苦中交付~

刷脸测年终奖,可点击左边链接或扫描下面二维码体验。

二维码

最后,希望以上分享能够对大家以后的开发有所帮助,谢谢~

Last Updated: 4/10/2019, 11:59:24 PM