版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wanshaobo888/article/details/89435623
最新更新时间:2019年04月21日17:56:04
《猛戳-查看我的博客地图-总有你意想不到的惊喜》
本文内容:H5端相机拍照原理、图片压缩方法、onload事件异步方法的同步处理、onload事件
多个
异步方法的同步处理、使用react-cropper实现图片预览和裁剪、使用vue-cropper实现图片预览和裁剪、base64和blob
1、H5端相机拍照原理
设置input标签的属性如下,capture为空会让用户选择本地文件或者拍照,onChange事件直接将图片文件转换为base64
<html>
<body>
//react组件
<input
type="file"
onChange={(e)=>{this.onChange(e)}}
className={styles.getImg}
id="fileinput"
ref='onChange'
accept="image/*"
capture="camera"
/>
</body>
<script>
onChange(e){
let file = e.currentTarget.files[0];
//console.log('图片原文件信息',file);
let fReader=new FileReader();
fReader.readAsDataURL(file);
fReader.onload=function(e) {
console.log('图片转化成base64的大小',this.result.length/1024,'kb')
}
}
</script>
</html>
注意:Change事件触发有两个必要条件:值改变、失去焦点。所以从本地第二次选择同一张照片时,不触发。
原生input样式不好看的解决方案
方案一:使用label元素来触发一个隐藏的file input元素
方案二:设置顶层input元素的透明度为零,设置底层元素为自己需要的可见样式,或者图片
2、图片压缩方法
//异步压缩方法
function asyncCompression(base64){
//初始化压缩率 0.1-表示将原图10M变成1M 10-表示将原图1M变成10M
let compressionRatio = 0.1;
let img = new Image();
img.src = base64;
//异步
img.onload = function () {
//生成比例
let width = img.width, height = img.height;
console.log('图片原始宽高',width,height)
//生成canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
//第一次粗压缩
let base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第一次粗压缩',base64.length/1024,'kb,压缩率',compressionRatio);
//第二次细压缩
while(base64.length/1024 > 200 && compressionRatio > 0.05){
compressionRatio -= 0.01;
base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第二次细压缩',base64.length/1024,'kb,压缩率',compressionRatio)
}
//第三次精细压缩 ...
//压缩后的结果 返回base64 此处是异步返回
return base64
};
}
3、onload事件异步方法的同步处理
方案一:采用Promise对象实现同步压缩方法
function promiseCompression(base64){
return new Promise((resolve,reject)=>{
//初始化压缩率 0.1-表示将原图10M变成1M 10-表示将原图1M变成10M
let compressionRatio = 0.1;
let img = new Image();
img.src = base64;
//异步
img.onload = function () {
//生成比例
let width = img.width, height = img.height;
console.log('图片原始宽高',width,height)
//生成canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
//第一次粗压缩
let base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第一次粗压缩',base64.length/1024,'kb,压缩率',compressionRatio);
//第二次细压缩
while(base64.length/1024 > 200 && compressionRatio > 0.05){
compressionRatio -= 0.01;
base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第二次细压缩',base64.length/1024,'kb,压缩率',compressionRatio)
}
//第三次精细压缩 ...
//压缩后的结果 返回base64
resolve(base64)
};
})
}
//使用
promiseCompression(base64).then((res)=>{
//send data to server
console.log('压缩后的base64',res)
})
方案二:采用Aasync 函数实现同步压缩方法
async function asyncCompression(base64) {
//初始化压缩率 0.1-表示将原图10M变成1M 10-表示将原图1M变成10M
let compressionRatio = 0.1;
let img = new Image();
img.src = base64;
//异步
img.onload = function () {
//生成比例
let width = img.width, height = img.height;
console.log('图片原始宽高',width,height)
//生成canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
//第一次粗压缩
let base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第一次粗压缩',base64.length/1024,'kb,压缩率',compressionRatio);
//第二次细压缩
while(base64.length/1024 > 200 && compressionRatio > 0.05){
compressionRatio -= 0.01;
base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第二次细压缩',base64.length/1024,'kb,压缩率',compressionRatio)
}
//第三次精细压缩 ...
//压缩后的结果 返回base64
return base64
};
}
//使用
asyncCompression(base64).then((res)=>{
//send data to server
console.log('压缩后的base64',res)
})
4、onload事件多个
异步方法的同步处理
方案一:采用Promise.all()实现多个异步方法的同步处理
function promiseCompression(base64){
return new Promise((resolve,reject)=>{
//初始化压缩率 0.1-表示将原图10M变成1M 10-表示将原图1M变成10M
let compressionRatio = 0.1;
let img = new Image();
img.src = base64;
//异步
img.onload = function () {
//生成比例
let width = img.width, height = img.height;
console.log('图片原始宽高',width,height)
//生成canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
//第一次粗压缩
let base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第一次粗压缩',base64.length/1024,'kb,压缩率',compressionRatio);
//第二次细压缩
while(base64.length/1024 > 200 && compressionRatio > 0.05){
compressionRatio -= 0.01;
base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第二次细压缩',base64.length/1024,'kb,压缩率',compressionRatio)
}
//第三次精细压缩 ...
//压缩后的结果 返回base64
resolve(base64)
};
})
}
let promiseList = [];
[1,2,3].forEach(()=>{
//三个异步等待同步同步
promiseList.push(promiseCompression(base64))
})
//使用
Promise.all(promiseList).then((res)=>{
//send data to server
console.log('压缩后的base64',res)
})
方案二:采用Aasync 函数的await命令实现多个异步方法的同步处理
function promiseCompression(base64){
return new Promise((resolve,reject)=>{
//初始化压缩率 0.1-表示将原图10M变成1M 10-表示将原图1M变成10M
let compressionRatio = 0.1;
let img = new Image();
img.src = base64;
//异步
img.onload = function () {
//生成比例
let width = img.width, height = img.height;
console.log('图片原始宽高',width,height)
//生成canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
//第一次粗压缩
let base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第一次粗压缩',base64.length/1024,'kb,压缩率',compressionRatio);
//第二次细压缩
while(base64.length/1024 > 200 && compressionRatio > 0.05){
compressionRatio -= 0.01;
base64 = canvas.toDataURL('image/jpeg', compressionRatio);
console.log('第二次细压缩',base64.length/1024,'kb,压缩率',compressionRatio)
}
//第三次精细压缩 ...
//压缩后的结果 返回base64
resolve(base64)
};
})
}
async function asyncCompression() {
let base64Data = ['1','2','3',]
let base64List = [];
base64List[0] = await promiseCompression(base64Data[0]);
base64List[1] = await promiseCompression(base64Data[1]);
base64List[2] = await promiseCompression(base64Data[2]);
return base64List
}
//使用
asyncCompression().then((res)=>{
//send data to server
console.log('压缩后的base64',res)
})
方案三:不借助第三方API实现多个异步方法的同步处理
//页面加载过程中显示加载蒙层,当3个异步http请求都成功后,隐藏加载蒙层
this.asyncGetDataStatus = [false,false,false]
componentDidMount(){
getDataFromDB01().then(res => {
this.asyncGetDataStatus[0] = true;
this.closeLoading();
})
getDataFromDB02().then(res => {
this.asyncGetDataStatus[1] = true;
this.closeLoading();
})
getDataFromDB03().then(res => {
this.asyncGetDataStatus[2] = true;
this.closeLoading();
})
}
closeLoading(){
if(this.asyncGetDataStatus.every((item) => item)){
this.setState({displayLoading: false})
}
}
5、使用react-cropper实现图片预览和裁剪
import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
getCropperData(){
//base64
return this.refs.cropper.getCroppedCanvas().toDataURL()
}
render(){
return <div>
<Cropper
className={styles.crop}
ref='cropper'
src={this.state.cropperData}
style={{maxHeight: 400, width: '100%'}}
//0-默认-没有任何限制 1-限制裁剪框不超过canvas画布边缘 2-如果是长图-限制图片不超过cropper的最高可视区域-同时裁剪框不超过canvas画布边缘
viewMode={2}
dragMode='none'
minCanvasWidth={285}
//隐藏棋盘背景色
background={false}
//裁剪框内部的横竖虚线可见
guides={true}
//裁剪框内部的十字线可见
center={false}
//可旋转原图
rotatable={true}
//可缩放原图
scalable={true}
//crop={(e)=>{this.crop(e)}}
/>
</div>
}
6、使用vue-cropper实现图片预览和裁剪
import VueCropper from 'vue-cropper'
Vue.use(VueCropper)
getCropperData(){
// 获取截图的base64 数据
this.$refs.cropper.getCropData((data) => {
// do something
console.log(data)
})
// 获取截图的blob数据
this.$refs.cropper.getCropBlob((data) => {
// do something
console.log(data)
})
}
<template>
<vueCropper
ref="cropper"
:img=""
:outputSize=""
:outputType=""
:info=""
:full=""
:canScale=""
:canMove=""
:canMoveBox=""
:original=""
:autoCrop=""
:fixedBox=""
:fixed=""
@imgLoad="imgLoad">
</vueCropper>
</template>
7、base64和blob
//base64的格式如下
let base64 = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAFA3PEY8MlBGQUZaVVBfeMiCeG5uePWvuZHI////////////////////////////////////////////////////2wBDAVVaWnhpeOuCguv/////////////////////////////////////////////////////////////////////////wAARCAAmACcDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEC/8QAGBABAQADAAAAAAAAAAAAAAAAAAIBEjH/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAwL/xAAXEQEBAQEAAAAAAAAAAAAAAAAAAQIR/9oADAMBAAIRAxEAPwCgMLgAAAAHQAAAAAAAAZutcAKZk4lq2V//2Q=="
function base64URLtoBlobUrl(base64){
const arr = base64.split(',');//['data:image/jpeg;base64','/9j/4AAQSkZJRgA...']
const mime = arr[0].match(/:(.*?);/)[1];//"image/jpeg"
const bstr = atob(arr[1])
let n = bstr.length;//316
const u8arr = new Uint8Array(n);//Uint8Array(316) [255,216,255,224,0,16,74,70,73,70,0,1,1,0,0,1,0,1,0,0,255,219,0,67,0,80,55,60,70,60,50,80,70,65,70,90,85,80,95,120,200,130,120,110,110,120,245,175,185,145,200,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,219,0,67,1,85,90,90,120,105,120,235,130,130,235,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,192,0,17,8,0,38,0,39,3,1,34,0,2,17,1,3,17,1,255,196,0,22,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,255,196,0,24,16,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,18,49,255,196,0,22,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,255,196,0,23,17,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,17,255,218,0,12,3,1,0,2,17,3,17,0,63,0,160,48,184,0,0,0,7,64,0,0,0,0,0,1,155,173,112,2,153,147,137,106,217,95,255,217]
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
const blob = new Blob([u8arr], { type: mime });//{size:316,type:"image/jpeg"}
const src = window.URL.createObjectURL(blob) //"blob:http://0.0.0.0:3001/24704350-26e1-4a0e-82de-d8d5bb5d954a"
return src
}
console.log(base64URLtoBlobUrl(base64));
//blob的格式如下
let u8arr = [255,216,255,224,0,16,74,70,73,70,0,1,1,0,0,1,0,1,0,0,255,219,0,67,0,80,55,60,70,60,50,80,70,65,70,90,85,80,95,120,200,130,120,110,110,120,245,175,185,145,200,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,219,0,67,1,85,90,90,120,105,120,235,130,130,235,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,192,0,17,8,0,36,0,36,3,1,34,0,2,17,1,3,17,1,255,196,0,21,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,255,196,0,23,16,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,17,33,49,255,196,0,22,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,255,196,0,20,17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,218,0,12,3,1,0,2,17,3,17,0,63,0,160,33,184,0,45,12,1,0,232,0,2,224,64,16,0,0,0,0,127,255,217]
const blob = new Blob(u8arr, { type: "image/jpeg" })
function blobToBase64Url(blob){
const promise = new Promise((resolve, reject) => {
const file = new FileReader()
file.readAsDataURL(blob)
file.onload = e => {
resolve(e.target.result)
}
})
return promise
}
blobToBase64Url(blob).then((res)=>{
console.log(res);//"data:image/jpeg;base64,MjU1MjE2MjU1MjI0MDE2NzQ3MDczNzAwMTEwMDEwMTAwMjU1MjE5MDY3MDgwNTU2MDcwNjA1MDgwNzA2NTcwOTA4NTgwOTUxMjAyMDAxMzAxMjAxMTAxMTAxMjAyNDUxNzUxODUxNDUyMDAyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyNTUyMTkwNjcxODU5MDkwMTIwMTA1MTIwMjM1MTMwMTMwMjM1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MjU1MTkyMDE3ODAzNjAzNjMxMzQwMjE3MTMxNzEyNTUxOTYwMjEwMTEwMDAwMDAwMDAwMDAwMDAxMjU1MTk2MDIzMTYxMTExMDAwMDAwMDAwMDAwMDE3MzM0OTI1NTE5NjAyMjExMTEwMDAwMDAwMDAwMDAwMDIzMjU1MTk2MDIwMTcxMDAwMDAwMDAwMDAwMDAwMDI1NTIxODAxMjMxMDIxNzMxNzA2MzAxNjAzMzE4NDA0NTEyMTAyMzIwMjIyNDY0MTYwMDAwMTI3MjU1MjE3"
})
参考资料
感谢阅读,欢迎评论^-^