100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 移动端/手机端 完成图片旋转 压缩 剪裁 上传

移动端/手机端 完成图片旋转 压缩 剪裁 上传

时间:2023-09-20 15:03:45

相关推荐

移动端/手机端 完成图片旋转 压缩 剪裁 上传

本篇文章主要介绍移动端/手机端图片的旋转、压缩、剪裁、上传

这个功能的实现已经好了几次方案流程了,对最终的方案流程进行简述

实现功能的主要方法/思想

1.图片的选取主要是通过input实现

2.图片的压缩通过画布、lrz共同实现

3.图片的旋转通过画布、react-cropper共同实现

4.图片的剪裁用react-cropper实现

5.图片的上传直接调用后端接口实现

图片的选取功能的实现

图片选取功能直接用input(其实也可以用antd的upload,但是产品要求区分拍照上传/相册上传,所以只能用input),具体代码如下

<div className="a">拍照/从相册选择</div><input id="realupload-choice1" type="file" accept="image/*" onChange={handleTakePhoto1} className="b"/>.a{width: 90%;height: 16%;position: absolute;top:50%;left: 5%;background-color: white;display: flex;justify-content: center;align-items: center;}.b{width: 90%;height: 16%;position: absolute;top:50%;left: 5%;background-color: white;display: flex;justify-content: center;align-items: center;opacity: 0;}

上面的代码中,由于input不太好看,所以我在相同的位置放置了一个大小一样的div,又将input的设置为透明,这样在使用的时候,只能看到div,但是实际点击的是input(类似下图效果)

input中,accept="image/*"规定了只接受图片,onChange={handleTakePhoto1}则是获取图片后,会主动调动handleTakePhoto函数

图片的压缩功能的实现

压缩功能使用了画布 lrz实现(画布实际上完成了图片的压缩/旋转,代码一并奉上)

function rotateBase64Img(src, edg){return new Promise((resolve, reject) =>{var canvas = document.createElement("canvas");var ctx = canvas.getContext("2d");var image = new Image();image.src = src;image.onload = function (){var widthNum=image.width/400var heightNum=image.height/400let proportion=null //缩小系数,使得缩小后短边等于400if (widthNum<heightNum) {proportion=widthNum}else{proportion=heightNum}var canWidth=image.width/proportionvar canHeight=image.height/proportionif (edg==0||edg==180) {canvas.width=canWidthcanvas.height=canHeightctx.translate(canWidth/2, canHeight/2)ctx.rotate(edg*Math.PI/180);ctx.drawImage(image, -canWidth/2, -canHeight/2,canWidth,canHeight);const picBase64RotatedTemp = canvas.toDataURL()resolve(picBase64RotatedTemp)}else if (edg==-90||edg==90) {canvas.width=canHeightcanvas.height=canWidthctx.translate(canWidth/2, canHeight/2)ctx.rotate(edg*Math.PI/180);ctx.drawImage(image, -canHeight/2, -canWidth/2,canWidth,canHeight);const picBase64RotatedTemp = canvas.toDataURL()resolve(picBase64RotatedTemp)}}})}

稍微解释下上述的代码入参是图片的base64、旋转角度(只支持90的倍数),输出是picBase64RotatedTemp,即压缩旋转后图片的base64

功能的实现比较简单,我把画布的长宽设置为400左右(长宽中短的设置为400),然后再通过ctx.drawImage将图片的长宽设置的和画布一样

旋转的话,要注意两点,一个是旋转远点,一个是画布是否要旋转

旋转原点通过ctx.translate(canWidth/2, canHeight/2)设置在画布的中心,实现中心旋转

画布是否要旋转取决去旋转角度,如果是180度,画布是不用旋转的,长宽和原来一致,如果是-90/90则长宽需要颠倒,所以在代码中进行了一个判断

除了画布压缩外,还有lrz压缩,代码如下

function compress(file,quality1){return new Promise((resolve, reject) => {lrz(file,{quality: quality1}).then((res)=>{console.log('压缩后文件的大小为',res.fileLen)console.log('压缩后文件的base64d大小为',res.base64Len)const piclrzed = res.base64resolve(piclrzed)})})}

lrz压缩,入参为图片/压缩系数,出参为压缩后图片的base64

图片的旋转功能的实现

图片旋转主要是有些手机在选取图片后,会发生旋转,所以我们要把系统的旋转纠正过来,就是逆向旋转回去,这牵扯到两个问题,判断手机型号,获取图片自带的旋转属性Orientation

由于我是将图片直接放入到画布中的,一般的手机图片是没问题的(IOS、华为、小米等都没问题,三星不行),所以首先是手机的判断,借助的是useragent

const iudgePhoneModel=()=>{var device_type = navigator.userAgentvar device_type_string = device_type.toString()if (device_type_string.indexOf("SM-")) {setPhoneBrand('samsung')}else{setPhoneBrand('no_samsung')}}

上述代码中,我获取了用户的userAgent,再通过判断是否包含"SM-"确定手机型号是否为三星(这么判断其实不是很严谨)

然后是图片旋转属性Orientation的获取

function ruturnOrientation(file){return new Promise((resolve, reject) => {EXIF.getData(file, function () {try {EXIF.getAllTags(file)const orientation = EXIF.getTag(file, 'Orientation')resolve(orientation)} catch (e) {reject(e)}})})}

上述代码用到exif,在一开始的时候要引入一下

import EXIF from "exif-js";

最后就是,当手机是三星的时候,先将图片放入画布,然后进行旋转,其他手机放入画布后不旋转(也就是旋转0读),下面代码中picOrientation.current为图片的旋转属性Orientation,reverseRotation为逆旋转角度

let reverseRotation=null //逆旋转的角度if (phoneBrand!=='no_samsung') {switch (picOrientation.current){case 1:reverseRotation=0breakcase 3:reverseRotation=0breakcase 6:reverseRotation=0breakcase 8:reverseRotation=0break}}else if (phoneBrand=='samsung') {switch (picOrientation.current){case 1:reverseRotation=0breakcase 3:reverseRotation=180breakcase 6:reverseRotation=-90breakcase 8:reverseRotation=90break}}

画布的旋转已经在上面画布的代码中体现,react-cropper的旋转,会在下一个目录中体现

图片的剪裁功能的实现

图片的剪裁主要通过react-cropper实现

import Cropper from 'react-cropper'import "cropperjs/dist/cropper.css"<div className='cropperimg-entire-pic'><Cropper// 裁剪框的宽高比aspectRatio={1} // 裁剪框内的虚线guides={false} // 图片来源src={base64Temp.current}// 剪裁后的输出// onInitialized={onCropperInit}ref={cropperRef}// 是否通过拖动来调整剪裁框的大小cropBoxResizable={false}// 定义cropper的拖拽模式dragMode={'move'}// 是否通过拖拽来移动剪裁框cropBoxMovable={false}// 定义自动裁剪面积大小(百分比)和图片进行对比autoCropArea={1}// 显示容器的网格背景(就是后面的马赛克)background={false}/></div>

上述代码中,各个属性的作用已经写在注释里了,主要说下两个

第一个是src,把要剪裁图片的base64传给src属性就可以了

第二个是剪裁后图片的输出,在早期版本中,使用的是ref={cropperRef}来实现,后来在插件2.0版本开始使用onInitialized={onCropperInit}来输出,再后来,官方又把ref={cropperRef}恢复了,所以现在两个方法都可以输出剪裁后的图片,由于我还要对剪裁后的图片进行旋转,所以使用了ref={cropperRef}

使用react-cropper旋转特别简单,代码如下,随便找个按钮激活下就行了

/**逆时针旋转剪裁器*/const antiClockwiseRotateCropper = () => {cropperRef.current.cropper.rotate(-90);}/**顺时针旋转剪裁器*/const clockwiseRotateCropper = () => {cropperRef.current.cropper.rotate(90);}

图片的上传功能的实现

图片的上传只要简单调用后端接口就行了,但是里面也有一个小坑

//cropper读取图片的base64cropperRef.current.src//corpper完成所有处理后,图片的base64cropperRef.current.cropper.getCroppedCanvas().toDataURL()

上面第一个base64是cropper接收到的图片的base64,也就是处理前的。第二个base64是处理后的,在本例中就是经过旋转、剪裁后图片的base64,所有如果要将处理后的结果传给后端,应该使用cropperRef.current.cropper.getCroppedCanvas().toDataURL()

一些可能用到的小工具

1.file转为base64

/**获取用户上传图片的base64,file转为base64*/ function getPicBase64(file){return new Promise((resolve, reject) => {const reader = new FileReader();reader.readAsDataURL(file);reader.onloadend = function(e) {const picBase64Temp = e.target.resultresolve(picBase64Temp)};})}

2.将base64转换为文件

/**将base64转换为文件*/ function dataURLtoFile(dataurl, filename) {var arr = dataurl.split(','),mime = arr[0].match(/:(.*?);/)[1],bstr = window.atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], filename, {type: mime });}

3.将时间以规定的格式输出

/**将时间以规定的格式输出*/function dateFormat(fmt, date) {var o = {"M+" : date.getMonth()+1, //月份 "d+" : date.getDate(),//日 "h+" : date.getHours(), //小时 "m+" : date.getMinutes(), //分 "s+" : date.getSeconds(), //秒 "q+" : Math.floor((date.getMonth()+3)/3), //季度 "S" : date.getMilliseconds() //毫秒 }; if(/(y+)/.test(fmt)) fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length)); for(var k in o) if(new RegExp("("+ k +")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); return fmt; }

总结

移动端/手机端 完成图片旋转 压缩 剪裁 上传,坑挺多的,我把踩过的坑都写在这篇文章里了,还有异步的一个坑没有写,下一篇文章会写一下

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。