Reya滴水心 |
发表于 10个月前 阅读 10304 收藏 283 点赞 31 评论 21 |
【粉丝福利】:web前端基础到高级实战免费在线直播教学>>> |
摘要: 移动端H5,调用input原生方法,实现拍照,并将图片压缩上传。 |
背景:移动端H5项目,需要实现调用手机拍照,并将图片压缩上传功能。 |
1页面样式: |
1 上传图片有原生的方法<input type= "file" accept= "image/*" >,如果只想要拍照上传,不希望用户选择图片上传,可以通过添加capture属性,该属性值有camcorder/microphone/camera...,此处选择camera。PS:该属性存在浏览器兼容性问题,不是所有的浏览器都支持。 |
<input type= "file" accept= "image/*" capture= "camera" > |
2 因为原生file样式不满足要求,需要进行相应的处理,此时使用定位,在input上面放置我们想要的页面效果。然后当点击上面的元素,就可以触发我们的input进行图片上传。此时的问题是:当点击input上面的元素,需要事件穿透,即相当于点击input。则借助于css3新属性pointer-events。 |
//使用cursor进行事件穿透,来阻止元素成为鼠标事件的目标 |
button{ |
cursor:pointer; |
pointer-events:none; |
} |
----此时图片上传的样式已经处理好了---- |
代码片段: |
<style > |
*{ |
padding: 0; |
margin: 0; |
} |
.wrapper{ |
width: 320px; |
height: 50px; |
margin: 20px auto; |
position: relative; |
border: 1px solid #f0f0f0; |
} |
input{ |
width: 100px; |
height: 30px; |
} |
button{ |
position: absolute; |
cursor: pointer; |
pointer-events: none; |
width: 100px; |
height: 30px; |
left: 0; |
top: 0; |
} |
a{ |
pointer-events: none; |
} |
.img{ |
border: 1px solid #ccc; |
padding: 10px; |
} |
</style > |
<div class = "wrapper" > |
<input type = "file" accept= "image/*" capture= "camera" id= "img" /> |
<button >上传照片 </button > |
</div > |
2 图片压缩处理: |
1 因为要做的是手机拍照上传,现在的手机拍照片都很大,比如小米4S,大小在3M以上,如果原图上传,太消耗用户流量,于是要解决图片压缩的问题。 |
2 通过change事件,监听图片上传,通过readerAsDataURL获取上传的图片。 |
document.getElementById( 'img' ).addEventListener( 'change' , function () { |
var reader = new FileReader(); |
reader.onload = function (e) { |
//调用图片压缩方法:compress(); |
}; |
reader.readAsDataURL(this.files[0]); |
console.log(this.files[0]); |
var fileSize = Math. round ( this.files[0].size/1024/1024) ; //以M为单位 |
//this.files[0] 该信息包含:图片的大小,以byte计算 获取size的方法如下:this.files[0].size; |
}, false); |
3 对上传的图片进行压缩,需要借助于canvas API,调用其中的canvas.toDataURL(type, encoderOptions); 将图片按照一定的压缩比进行压缩,得到base64编码。重点来了:压缩策略:先设置图片的最大宽度 or 最大高度,一般设置其中一个就可以了,因为所有的手机宽高比差别不是很大。然后设置图片的最大size,allowMaxSize,根据图片的实际大小和最大允许大小,设置相应的压缩比率。 |
//最终实现思路: |
1、设置压缩后的最大宽度 or 高度; |
2、设置压缩比例,根据图片的不同size大小,设置不同的压缩比。 |
function compress(res, fileSize ) { //res代表上传的图片,fileSize大小图片的大小 |
var img = new Image(), |
maxW = 640; //设置最大宽度 |
img.onload = function () { |
var cvs = document.createElement( 'canvas' ), |
ctx = cvs.getContext( '2d' ); |
if (img.width > maxW) { |
img.height *= maxW / img.width; |
img.width = maxW; |
} |
cvs.width = img.width; |
cvs.height = img.height; |
ctx.clearRect(0, 0, cvs.width, cvs.height); |
ctx.drawImage(img, 0, 0, img.width, img.height); |
var compressRate = getCompressRate(1, fileSize ); |
var dataUrl = cvs.toDataURL( 'image/jpeg' , compressRate); |
document.body.appendChild(cvs); |
console.log(dataUrl); |
} |
img.src = res; |
} |
function getCompressRate(allowMaxSize, fileSize ){ //计算压缩比率,size单位为MB |
var compressRate = 1; |
if ( fileSize /allowMaxSize > 4){ |
compressRate = 0.5; |
} else if ( fileSize /allowMaxSize >3){ |
compressRate = 0.6; |
} else if ( fileSize /allowMaxSize >2){ |
compressRate = 0.7; |
} else if ( fileSize > allowMaxSize){ |
compressRate = 0.8; |
} else { |
compressRate = 0.9; |
} |
return compressRate; |
} |
3 图片上传给服务器: |
1 图片已经压缩完成了,但是压缩后的图片不是File,而是一个base64编码,于是乎两个选择:1、以String的形式将base64编码上传给服务器,服务器转存base64为img图片;2、前端将base64转为File图片类型,上传给服务器。 |
2 方法一:压缩之后直接上传base64给后台,实现简单。 |
3 方法二:前端自己转存base64位File图片,这个对于前端 压力比较大。 |
原因:html5 canvas属于客户端API,没有权限去保存图片到硬盘,只有canvas.toDataURL()这一接口可导出画布的base64编码,以提供给服务器进行处理保存。所以如果要将base64编码转成图片需要借助于nodeJs。因为nodeJS有相关文件处理的API。 |
//使用nodeJS将base64转化成图片 |
var express = require ( 'express' ); |
var fs = require ( "fs" ); |
var app = module.exports = express(); |
function dataToImage(dataUrl){ |
var base64Data = dataUrl.replace(/^data:image\/\w+;base64,/, '' ); |
var dataBuffer = new Buffer(base64Data, 'base64' ); |
fs.writeFile( 'out.jpg' ,dataBuffer, function (err){ |
if (err){ |
console.log(err); |
} else { |
console.log( 'Success...' ); |
} |
}); |
} |
dataToImage( 'data:image/jpeg;base64,/9...' ); //图片完整base64过长,所以省略... |
if (!module.parent){ |
app.listen(8000); |
console.log( 'Express started on port 8000' ); |
} |
Summary:如果使用nodeJS,需要单独部署nodeJS代码到服务器,整个逻辑会比较麻烦。综合比较两种方法,推荐使用第一种方法,直接传base64给服务器,后台处理相应的转化!demo: http: //www.zhangyixia.com/image-upload/ |
可直接扫描二维码: |
|
相关知识科普: |
图像一般由两部分组成:一部分是数据本身,他记录了每个像素的颜色值,另一部分是文件头,这里记录着形如图像的宽度,高度等信息。 |
不同图片类型及适用场景: |
不同图片类型: |
GIF:无损压缩,体积小,支持透明效果,缺点:色彩效果最低,用gif保存鲜艳的图片的话会让网站看上去非常可怕。 |
PNG:无损压缩,可渐变透明,缺点:不如JPG颜色丰富,同样的图片体积也比JPG略大。 |
JPG/JPEG:色彩还原性好,可以在照片不明显失真的情况下,大幅度压缩图片格式,所以体积不会很大。缺点:不支持透明 |
适用场景: |
当图片色彩鲜艳,基本没有透明效果的时候,选择jpg/jpeg。 |
当图片色彩鲜艳,透明效果较多的情况下,选择png; |
当图片色彩比较单一情况下,可以选择gif; |
toDataURL,查找原生压缩图片的方法。type支持image/webp格式 |
canvas.toDataURL(type, encoderOptions); |
Base64编码:将三个8Bit的字节转换为四个6Bit的字节。 |
|
参考资料: |
http: //www.imys.net/20150916/webapp-input-use-camera.html |
http: //www.oxxostudio.tw/articles/201409/pointer-events.html |
https: //segmentfault.com/a/1190000005364299 |