文章目录

  • 调用摄像头拍照
  • 本地预览
  • 旋转压缩
  • 上传


功能需求是移动端扫描二维码之后,跳转到在线网页进行自拍,然后上传拍照结果。

调用摄像头拍照

在h5中,使用input type="file"的 capture="camera"这个属性就可以调用摄像头
在这里我们使用的是ElementUI的组件,实际这个组件里面是包含一个input type="file"的元素的
所以我们只需要修改这个元素的capture和accept属性就可以了
但是vue本身是不推荐修改dom元素的,所以可以引入jQuery方便操作

  1. 在package.json中引入jquery
"jquery":"^3.4.1",
  1. 在main.js中引用jquery
import $ from 'jquery'
  1. 在调用摄像头的vue文件中, 以下是部分代码
    html部分的代码如下
<el-upload
	class="mobile-avatar-uploader"
	ref="uploadVisitor"
	:action="upLoadUrl"
	:auto-upload="false"
	:on-change="photoPreview"
    :show-file-list="false">
	    <img v-if="editForm.visitorPhoto" :src="editForm.visitorPhoto" class="mobile-avatar">
        <i v-else class="el-icon-camera mobile-avatar-uploader-icon"></i>
</el-upload>

在加载页面的时候使用jquery的代码来获取input file的dom节点并加上accept和camera属性
mounted代码如下:

let obj = $("#avatarDiv input");
    obj.attr('accept', 'image/*');
    obj.attr('capture', 'camera');

ref属性用来手动操作文件调用默认上传方法,可以不设置(如果调用默认上传方法需要设置)
action属性用来设置文件默认上传地址,一般不为空,没用的话随意填写

本地预览

ElementUI的el-upload组件,在官网上有用户头像上传的实例,同时也提供了auto-upload属性
如果把auto-upload属性改为false,那么选中照片之后不会自动上传到服务器,但是同时也无法预览
所以只好给on-change加上一个方法,在选中文件之后,手动将文件显示出来
组件代码见第一部分
下面是on-change绑定的预览方法

photoPreview(file, fileList) {
                if (file.status == 'ready') {
                    var _this = this;
                    var event = event || window.event;
                    var file = event.target.files[0];
                    Exif.getData(file, function () {
                        _this.orientation = Exif.getTag(this, 'Orientation');
                    });

                    var reader = new FileReader();
                    _this.fileType = file.type;
                    //转base64
                    reader.onload = function (e) {
                        _this.photoCompress(e.target.result, function (base64Codes) {
                            _this.editForm.visitorPhoto = base64Codes; //将图片路径赋值给src
                        });
                    };
                    reader.readAsDataURL(file);
                }
            },
  1. 通过file.status=='ready’判断是选择照片/拍照,而不是上传,因为不管是拍照还是上传都会触发on-change事件
  2. 通过event.target.files[0]获取拍照文件
  3. event||window.event主要是为了浏览器兼容,IE是window.event firefox是直接调用event
  4. FileReader对象 可能有些旧的浏览器不支持
  5. Exif之后再讲

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
FileReader实例拥有4个方法,3个用来读取文件,一个用来中断读取

  • abort 参数none 中断读取
  • readAsBinaryString 参数file 将文件读取为二进制码
  • readAsDataUrl 参数file 将文件读取为data:开头的字符串,实质就是Data URL 是将小文件(图像等)直接嵌入文档的方案,base64的方式由此来获得
  • readAsText file,[encoding] 文本方式读取文件,读取结果是文本的内容

读取结果会存储在FileReader的result属性中
处理事件简介:

  • onabort 中断时触发
  • onerror 出错时触发
  • onload 读取成功时触发
  • onloadend 读取完成时触发 无论成功失败
  • onloadstart 读取开始时触发
  • onprogress 读取中
    文件开始读取后无论成功失败都会填充result属性,读取失败result属性会赋值null,否则会填充读取结果

onload读取成功之后,直接将base64位赋值给img元素的src属性,就可以预览图片了!

旋转压缩

利用canvas将base64的文件压缩

  1. 手机前端摄像头拍照经常会拍出来的照片是横着的,这里引用exif来调整照片的方向
  2. 现在手机拍照的像素都很高,所以拍照之后图片文件都过大,所以需要压缩
/**
             * 图片压缩(利用canvas)
             * @param  path     图片路径
             * @param  obj      压缩配置width,height,quality,不传则按比例压缩
             * @param  callback  回调函数
             */
            photoCompress(path, callback) {
               var _this = this;
//                解决照片旋转问题
                let img = new Image();
                img.src = path;
                img.onload = function () {
                    var that = this;
                    //生成canvas
                    var canvas = document.createElement('canvas'),
                        ctx = canvas.getContext('2d');
                    // 默认按比例压缩
                    var degree = 0, drawWidth, drawHeight, width, height;
//                    drawWidth = that.naturalWidth;
//                    drawHeight = that.naturalHeight;
                    drawWidth = that.width / 2;
                    drawHeight = that.height / 2;
                    canvas.width = width = drawWidth;
                    canvas.height = height = drawHeight;
                    //判断图片方向,重置canvas大小,确定旋转角度,iphone默认的是home键在右方的横屏拍摄方式
                    if (_this.orientation != "" && _this.orientation != 1) {
                        switch (_this.orientation) {
                            //iphone横屏拍摄,此时home键在左侧
                            case 3:
                                degree = 180;
                                drawWidth = -width;
                                drawHeight = -height;
                                break;
                            //iphone竖屏拍摄,此时home键在下方(正常拿手机的方向)
                            case 6:
                                canvas.width = height;
                                canvas.height = width;
                                degree = 90;
                                drawWidth = width;
                                drawHeight = -height;
                                break;
                            //iphone竖屏拍摄,此时home键在上方
                            case 8:
                                canvas.width = height;
                                canvas.height = width;
                                degree = 270;
                                drawWidth = -width;
                                drawHeight = height;    
                                break;
                        }
                    }
                    //使用canvas旋转校正
                    ctx.rotate(degree * Math.PI / 180);
                    ctx.drawImage(that, 0, 0, drawWidth, drawHeight);

                    // 默认图片质量为0.4
                    // 回调函数返回base64的值
                    var base64 = canvas.toDataURL(_this.fileType, 0.4);
                    callback(base64);
                }
            },
  1. image的加载事件(onload),是在一张图片被完整加载后触发的事件,一般用这个方法加载图片后获取图片的宽和高

ios可能会遇到onload不执行的情况 可以利用img的complete属性或者在onload里面加上setTimeout延时来处理

  • complete是无论img的src是否有值,只要加载完image对象和属性, complete属性就会变成true,
  • 而onload事件需要图片有src并加载完成后,才会触发
  1. 在拍摄图片之后,就用Exif获取图像的原始数据,例如:拍照方向,拍摄事件,ISO感光度,GPS地理位置等数据
    然后使用canvas根据拍照方向在进行调整和压缩
  1. exif主要用来处理拍摄的照片,兼容主流浏览器,不支持IE10以下浏览器
  2. 首先在package里面引入exif "exif-js":"^2.3.0", exif提供的 API方法主要有
  • Exif.getData(img,callback) 获取图像数据
  • Exif.getTag(img,tag) 获取图像的某个数据
  • Exif.getAllTags(img) 获取图像的全部数据
  • Exif.pretty(img) 获取图像的全部数据,值以字符串的形式范围
    这里先用到getData这个方法来获取图像的数据
    然后在回调中用到了getTag这个方法,tag参数传的是’Orientation’,来获取拍摄方向
    orientation不同的值代表不同的方向,具体可以看代码中的注释

tag还有很多种参数 详情参考Exif的标识 连接地址http://code.ciaoca.com/javascript/exif-js/

  1. 将获取的拍摄方向存储在vue的数据中,然后在压缩图片的时候,根据拍摄方向来调整图片的方向
    这里使用了h5的canvas 画布来绘制图像,完成压缩和调整方向的功能

canvas简介
canvas是一个容器(画布),他的getContext()方法用来获取一个对象,这个对象提供了再画布上绘图的方法和属性。
canvas兼容ie9以及更高、chrome和safari等主流浏览器
创建canvas的方法一般如下
var canvas = document.createElement(‘canvas’) , ctx = canvas.getContext(‘2d’);

  • 然后通过rotate方法来旋转图片
    ctx.rotate(20*Math.PI/180); 代表顺时针旋转20度 canvas绘制图片旋转是按照左上角顶点来旋转的 所以drawImage的宽高才需要负数
    例如旋转90度之后,如果不调整其他参数,就看不到绘制图片了(因为应旋转到左边去了)
    所以要先把drawImage的height参数改成负数,让图片相对上移(绝对来说是往右移)
    然后修改画布的宽高(对换),这样就能把图片从横的画成竖着的(可以在w3school在线测试rotate方法就懂了)
  • 通过drawImage方法来绘制图片
    context.drawImage(img,x,y,width,height); 在画布上定位图像,并规定图像的宽度和高度:
    x y是画图的坐标起点 这里都设置为0
  • canvas的toDataURL方法则可以将绘制出来的图像转化为Base64的值
    toDataURL(fileType,opacity) 第一个参数代表文件类型,第二个参数代表转base64时压缩程度0~1 越小压缩越多

canvas的全部方法api参考http://www.w3school.com.cn/tags/html_ref_canvas.asp canvas用法非常丰富 不详细展开

旋转之后还需要调整画布和图像的宽和高(例如顺时针90度,画布的宽度和高度要交换)

这里在初始化宽高的时候,已经除以2了,意思是已经将图像的宽高都对半压缩
naturalHeight和naturalWeight指的是真实图片的宽高,是H5新增的属性,IE9以及以上 主流浏览器兼容
如果不支持的话还是使用width和height属性比较稳

上传

上传这里有两种方法

方法1:直接将压缩过的base64的图片信息上传(当前使用这个办法),base64就作为字符串直接在form表单中提交

方法2:将base64的文件再根据原始的file的名称和类型,重新转化成file,然后覆盖el-uploadhttp-request属性,这样就可以覆盖el-upload自带的上传文件方法,然后在提交按钮事件中加入this.$refs.uploadVisitor.submit();代码
手动上传图片即可。
覆盖el-upload自带的上传方法,需要使用FormData对象,然后用$.ajax来提交,这里没有采用这个方法就不贴代码了