H5-移动端相机拍照(读取本地相册)(PC端读取本地相册)、压缩、预览、裁剪、上传功能开发汇总

版权声明:本文为博主原创文章,未经博主允许不得转载。 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"
})

参考资料

感谢阅读,欢迎评论^-^

打赏我吧^-^

猜你喜欢

转载自blog.csdn.net/wanshaobo888/article/details/89435623