cropperjs图片裁剪及数据提交文件流互相转换详解

JavaScript/前端
248
0
0
2024-02-19
标签   JavaScript库

cropperjs的主要功能是图片裁剪,是一款前端常用的的图片裁剪工具,可根据相关api配置裁剪出符合自己业务需要的图片,具体使用如下:

  1. npm 引用
npm i cropperjs
  • 1
  • 业务中引入
import Cropper from 'cropperjs';
  • 1
  • 文件中单独引入方式
<link  href="/path/to/cropper.css" rel="stylesheet">
<script src="/path/to/cropper.js"></script>
  • 1
  • 2
  • 初始化图片裁剪
new Cropper(element, options)
  • 1

参数:

element:  需要裁剪的图片元素,一般指本地获取到的img展示标签
options: {
	aspectRatio: 1 / 1, // 裁剪框纵横尺寸比例
	autoCropArea: 1,  // 它应该是一个介于 0 和 1 之间的数字。定义自动裁剪区域大小(百分比), 默认80%
	viewMode: 1,  // 视图模式
	dragMode: "move", // 图片可移动 拖拽模式
	cropBoxMovable:false, //是否可以拖拽裁剪框
	preview:ele,// Dom元素,该元素的预览尺寸样式尽量和aspectRatio尺寸比例保持一致
	responsive: true, // 调整窗口大小时重新渲染裁剪器
	modal:  true,// 在图像上方和裁剪框下方显示黑色模态
	rotatable:  true,// 启用以旋转图像
	scalable: true, // 启用以缩放图像
	zoomable:  true,// 启用以缩放图像
	zoomOnTouch: true,  // 启用通过拖动触摸来缩放图像
	zoomOnWheel: true, //鼠标滚轮缩放
	cropBoxMovable: false, // 裁剪框可移动
	cropBoxResizable: false, // 裁剪框大小可调整
	resizable: false, // 是否允许改变裁剪框大小
	ready: Function,  // 裁剪实例准备完成回调,由于加载图片时有一个异步过程,所以大部分方法都应该在 ready 之后调用
	reset: Function,  // reset() 重置
	clear: Function,  // clear() 清除
	replace(url, hasSameSize): Function,  // url 图片地址, hasSameSize:Boolean,如果新图像与旧图像大小相同,则不会重建裁剪器,只会更新所有相关图像的 URL。这可用于应用过滤器
	...
}

配置项:

viewMode

type:

Number

default:

0

option:

0:无限制

1: 限制裁剪框不超过画布的大小。

2: 限制最小画布大小以适合容器。如果画布和容器的比例不同,最小画布将被其中一个维度中的额外空间包围。

3: 限制最小画布大小以填充容器。如果画布和容器的比例不同,容器将无法在其中一个维度中容纳整个画布。定义裁剪器的视图模式。

如果将viewMode设置为0,裁剪框可以延伸到画布之外,而值为1、2或3将裁剪框限制为画布的大小。viewMode为2或3将额外将画布限制为容器。当画布和容器的比例相同时,2和3之间没有差异。

一. 首先通过input file拿到的本地展示路径有两种:

1.base64格式

2.url格式

base64获取方式: 通过FileReader实例完成后的onload事件获取

url方式:URL.createObjectURL(img) -浏览器可识别的URL图片路径

uploadAwatar (value){ 
    // 图片上传成功后创建 URL
     const fileList = value.target.files; // input file文件
     let img = ele;  // 展示的裁剪图
     if (fileList.length) {
         // url 格式获取可识别的图片路径
         let imgUrl = URL.createObjectURL(fileList[0]);
         img.src = imgUrl;
         img.onload = function(){
             setCutImg (fileList[0].type); // 裁剪函数
         }
         // 转base64
         /**
          if (typeof FileReader === 'function') { 
	                const reader = new FileReader();
	                reader.readAsDataURL(fileList[0]);
	                reader.onload = (event) => {
	                    var Base64Val = event.target.result;
	                     img.src = Base64Val;
	                    console.log( Base64Val)
	                }
	          } **/
     }
 }	
二. 通过上面有了本地图片展示就满足了图片裁剪的条件,传入dom, 初始化图片裁剪
 /**
 *  @param setCutImg 初始化裁剪函数
 *  @param imgEle 本地获取到的图片展示元素
 */
setCutImg (type){
    let objOpt = {};
    let winW = cropperPhoto.data.winW - 20;  // 手机端两侧10px间距
    // 正方形头像裁剪,图片竖图,窄图始终以最小的边居中填充裁剪框
    if (imgEle.naturalWidth > imgEle.naturalHeight) {  // 横图
        objOpt = {
            minCropBoxHeight: winW,
            minCanvasHeight: winW,
            minContainerHeight: winW,
        }
    } else { // 竖图
        objOpt = {
            minCropBoxWidth: winW,
            minCanvasWidth: winW,
            minContainerWidth: winW,
        }
    }
    
    cropper = new Cropper(imgEle, {
        ...objOpt,
        aspectRatio: 1 / 1,
        autoCropArea: 1,
        viewMode: 1,
        dragMode: "move", //图片可移动
        cropBoxMovable:false, //是否可以拖拽裁剪框
        zoomOnTouch: true,  
        scalable: true,
        zoomable: true,
        cropBoxMovable: false, // 裁剪框可移动
        cropBoxResizable: false, // 裁剪框大小可调整
        resizable: false, // 是否允许改变裁剪框大小
        ready(){
            let _this = this;
                $('.upload-cut').off().on('click', '.cancel', ()=> { // 关闭时处理的逻辑...
                    _this.cropper && _this.cropper.destroy();  // 销毁实例
                }).on('click', '.sure', async ()=> {  // 确认裁剪的逻辑
                    let cas = _this.cropper.getCroppedCanvas({
                        width: winW,  // 根据需求配置最终裁完的图片宽
                        height: winW,  // 根据需求配置最终裁完的图片高
                        maxWidth: 500,   // 头像所以我这给的最大是500
                        maxHeight: 500, 
                        fillColor: '#fff', // 如果有空白背景色填充
                        imageSmoothingEnabled: false,                            
                    }).toBlob(async(blob) => {
                    	// blob 流提交
                        var formData = new FormData();
                        /* *  
                        // 方法一
                        let arr = new Array(blob);
                        console.log(arr, 'blob');
                        let file = new File(arr, 'file', { type: blob.type,});
                        console.log(file, 'file');
                        formData.append('avatarFile', file); */
                        blob.lastModifiedDate = new Date();
                        blob.name = 'file';
                        blob.filename = 'file';
                        formData.append('avatarFile', blob); // 后端以avatarFile取到blob数据流
                        console.log(formData, 'formData');  // formdata类型数据提交
                        commitData.call(this, formData)   // 数据提交
                    })
                    /**
                    // 方法二
                    // 拿到base64 转成 file流提交
                   	 let base64url = cas.toDataURL(type);
                     let fileData = dataURLtoFile(base64url);
                    console.log(fileData, 'fileData');
                    var formData = new FormData(); // formdata类型数据提交
                    formData.append('avatarFile', fileData);
                   commitData.call(this, formData)  // 数据提交
                   **/

                    /**
                    // 方法三
                    // base64 转blob流转file流提交
                    let base64url = cas.toDataURL(type);
                    // 调用如下
                    let blob = dataURLtoBlob(base64url);
                    let fileData = blobToFile(blob, "fileName");
                    // 上传file就可以了
                    console.log(fileData, 'fileData');
                    var formData = new FormData();  // formdata类型数据提交
                    formData.append('avatarFile', fileData);
                    commitData.call(this, formData) // 数据提交
                    **/
                })
        }
    });
}
function commitData(formData){
	let _this = this;
			 $.ajax({
           url: path,
           	method: "POST",
           	data: formData,
           	success: function(res){
           		....
           		 _this.cropper && _this.cropper.destroy(); // 裁剪完成释放空间销毁实例
           	},
           	error: function(err){
           		....
           		 _this.cropper && _this.cropper.destroy(); // 裁剪完成释放空间销毁实例
           	}
			})
 		}
		
		function dataURLtoFile (dataurl, filename = 'file') {  // base64转file流
    let arr = dataurl.split(',')
    let mime = arr[0].match(/:(.*?);/)[1] // 正则匹配文件类型
    let suffix = mime.split('/')[1]
    let bstr = atob(arr[1])
    let n = bstr.length
    let u8arr = new Uint8Array(n)
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
    }
    return new File([u8arr], `${filename}.${suffix}`, {type: mime})
},
function dataURLtoBlob(toDataURL) { // base64转blob
    var arr = toDataURL.split(","),
      mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
    u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], { type: mime });
},
function blobToFile(Blob, fileName) { // blob 模拟file流
    Blob.lastModifiedDate = new Date(); // 或者Date.now(), 文件最近一次的修改时间
    Blob.name = fileName; // 文件名
    return Blob;
}

new File(data, fileName, options);

第一个参数是个数组,数组项可以是 ArrayBuffer, String 等等,第二个参数fileName指文件名称,第三个options是配置项,支持 type 和 lastModified 属性,type 可以传入 text/plain, text/html 等,lastModified 默认为 Date.now()

实例上的属性

说明

lastModified

返回文件最后的修改时间 ,是个时间戳

lastModifiedDate

返回文件最后的修改时间,一个 Date 对象name文件名称

size

文件大小

webkitRelativePath

文件的本地路径或者

type

文件的MIME 类型

const file = new File(["foo"], "foo.txt", {
  type: "text/plain",
  lastModified: Date.now()
})
file.name // foo.txt

FileList

FileList 一个 File 的数组,它只有一个方法:

files.item(i) //  == files[i]
  • 1

new Blob(data, options);

与File构造函数类似,第一个参数是个数组,数组项可以是 ArrayBuffer, String 等等,第二个是配置项,最常用的就是 type 属性,可以传入 text/plain, text/html 等

属性和方法

说明

sizeBlob

对象中所包含数据的大小(字节)

type

一个字符串,表明该 Blob 对象所包含数据的 MIME 类型

slice(start, end)

返回一个新的 Blob对象,包含了源 Blob 对象中指定范围内的数据。和字符串的 slice 方法类似

stream()

返回一个能读取 blob 内容的 ReadableStream

text()

返回一个 promise 且包含 blob 所有内容的UTF-8格式的字符串

arrayBuffer()

返回一个promise且包含blob所有内容的二进制格式的 ArrayBuffer

const blob = new Blob(['hello world'], { type: 'text/plain' })
blob.text().then(console.log) // 'hello world'
  • 1
  • 2

FileReader

FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,可以读取 Blob 和 File 的数据。

一个简单的使用例子,初始化后,监听 load 事件,然后调用读取方法。

const reader = new FileReader();
reader.onload = function(evt) {
  console.log(evt.target.result);
};
reader.readAsText(file)

事件

说明

onabort

读取操作被中断事件

onerror

读取操作发生错误的事件

onload

读取操作完成的事件

onloadstart

该事件在读取操作开始时触发

onloadend

该事件在读取操作结束时(要么成功,要么失败)触发

onprogress

取 Blob 时触发

方法

说明

abort

中止读取readAsArrayBuffer开始读取数据,读取完后 result 是 ArrayBuffer 对象

readAsBinaryString

开始读取数据,读取完后 result 是二进制数据

readAsData

URL开始读取数据,读取完后 result 是 Base64 字符串

readAsText

开始读取数据,读取完后 result 是字符串

备注:还未亲自测试,但应该可以,值得参考