多年前介绍过一款浏览器上模拟图片裁剪,获取图片裁剪坐标的js插件:jcrop,很low的原文在此:https://blog.jjonline.cn/userInterFace/jquery_jcrop_img_corp.html,jcorp本身并不能执行图片的裁剪逻辑,但这个强悍的插件却可以通过拖拽很轻松的获取到需要执行图片裁剪的宽高和坐标数据,配合后端的譬如PHP脚本再真实执行图片的裁剪、缩放等操作,即可完成图片的裁剪逻辑。
以前的图片裁剪其真实裁剪过程是在后端执行的,那么能不能把图片真实裁剪的过程放在浏览器执行呢?随着HTML5和ECMAScript的不断发展,图片的读取、处理和裁剪完全在浏览器端完成是可行的,甚至配合Fromdata对象还可实现图片的ajax上传(使用XMLHttpRequest上传图片 )。
先上效果图,第一张图为选择图片裁剪并上传的操作浮层,第二张图为选中本地图片之后配合jcrop拖拽获取裁剪坐标,并配合canvas实现图片在浏览器端的裁剪过程,最后通过Fromdata对象ajax把裁剪后的图片上传到服务器端。
实现原理
File对象
File 对象代表一个文件,用来读写文件信息。它继承了 Blob 对象,或者说是一种特殊的 Blob 对象,所有可以使用 Blob 对象的场合都可以使用它。
最常见的使用场合是表单的文件上传控件(<input type="file">
),用户选中文件以后,浏览器就会生成一个数组,里面是每一个用户选中的文件,它们都是 File 实例对象。File实例化对象具有多个有用的属性,如下:
File.lastModified
:文件的最后修改时间
File.name
:文件名或文件路径
File.size
:文件大小(单位字节)
File.type
:文件的 MIME 类型
这样在一个文本上传控件出现变动事件时即可获取到用户选择上传的文件信息:文件路径、大小、类型以及没什么用途的最后修改时间。
FileReader对象
FileReader 对象用于读取 File 对象或 Blob 对象所包含的文件内容。现代高级浏览器原生提供一个FileReader构造函数,用来生成 FileReader 实例。FileReader对象有如下属性和事件:
FileReader.error
:读取文件时产生的错误对象信息
FileReader.readyState
:整数,表示读取文件时的当前状态。一共有三种可能的状态,0表示尚未加载任何数据,1表示数据正在加载,2表示加载完成。
FileReader.result
:读取完成后的文件内容,有可能是字符串,也可能是一个 ArrayBuffer 实例。
FileReader.onabort
:abort事件(用户终止读取操作)的监听函数。
FileReader.onerror
:error事件(读取错误)的监听函数。
FileReader.onload
:load事件(读取操作完成)的监听函数,通常在这个函数里面使用result属性,拿到文件内容。
FileReader.onloadstart
:loadstart事件(读取操作开始)的监听函数。
FileReader.onloadend
:loadend事件(读取操作结束)的监听函数。
FileReader.onprogress
:progess事件(读取操作进行中)的监听函数。
FileReader.abort()
:终止读取操作,readyState
属性将变成2
。
FileReader.readAsArrayBuffer()
:以 ArrayBuffer 的格式读取文件,读取完成后result
属性将返回一个 ArrayBuffer 实例。
FileReader.readAsBinaryString()
:读取完成后,result
属性将返回原始的二进制字符串。
FileReader.readAsDataURL()
:读取完成后,result
属性将返回一个 Data URL 格式(Base64 编码)的字符串,代表文件内容。对于图片文件,这个字符串可以用于<img>
元素的src
属性。注意,这个字符串不能直接进行 Base64 解码,必须把前缀data:*/*;base64,
从字符串里删除以后,再进行解码。
FileReader.readAsText()
:读取完成后,result
属性将返回文件内容的文本字符串。该方法的第一个参数是代表文件的 Blob 实例,第二个参数是可选的,表示文本编码,默认为 UTF-8。
FileReader对象主要用到的就是onload
事件和readAsDataURL
方法,通过用户选中上传的文件事件使用File对象获取到文件信息,然后使用FileReader对象读取到用户选中的文件信息,将用户所选的图片转换成base64编码的图片在页面中显示,接下来在显示的图片上初始化jcrop裁剪插件,用于获取裁剪坐标信息。古老的方案中必须将用户所选的图片上传至服务器,然后返回远程图片的url再予以显示。
Canvas元素和api
HTML5 <canvas>
标签用于绘制图像(通过脚本,通常是 JavaScript)。<canvas>
元素本身并没有绘制能力(它仅仅是个图形的容器),若要绘图则必须使用脚本来完成。
canvas提供的api有很多,具体详情可参考对应资料或下方提到的参考链接,对于浏览器端的图片裁剪仅需要用到drawImage
方法即可,该方法的的参数有多种形式,对于浏览器端的图片裁剪参数形式如下:
FormData对象
FormData对象就是一个表单对象,可以通过该对象自主构造一个发送表单。
FormData 提供以下实例方法:
FormData.get(key)
:获取指定键名对应的键值,参数为键名。如果有多个同名的键值对,则返回第一个键值对的键值。
FormData.getAll(key)
:返回一个数组,表示指定键名对应的所有键值。如果有多个同名的键值对,数组会包含所有的键值。
FormData.set(key, value)
:设置指定键名的键值,参数为键名。如果键名不存在,会添加这个键值对,否则会更新指定键名的键值。如果第二个参数是文件,还可以使用第三个参数,表示文件名。
FormData.delete(key)
:删除一个键值对,参数为键名。
FormData.append(key, value)
:添加一个键值对。如果键名重复,则会生成两个相同键名的键值对。如果第二个参数是文件,还可以使用第三个参数,表示文件名。
FormData.has(key)
:返回一个布尔值,表示是否具有该键名的键值对。
FormData.keys()
:返回一个遍历器对象,用于for...of循环遍历所有的键名。
FormData.values()
:返回一个遍历器对象,用于for...of循环遍历所有的键值。
FormData.entries()
:返回一个遍历器对象,用于for...of循环遍历所有的键值对。如果直接用for...of循环遍历 FormData 实例,默认就会调用这个方法。
Jcrop插件
Jcrop是一个裁剪模拟框插件,可以通过简单的拖拽、移动获取到裁剪后的尺寸、坐标信息,固网地址:http://deepliquid.com/content/Jcrop.html,github地址:https://github.com/tapmodo/Jcrop
这个库最稳定的版本已经比较老了,还是0.9.12版,中途出过2.0版本,不过api有变动后来官网也被撤下,还是使用最稳定的0.9.12版,笔者写此文时0.9系列的git库似乎有发了新版,未发现有更多变动。
Jcrop的使用方法不过多介绍,请参考对应文章,本次裁剪主要使用两个核心回调事件:
onSelect
:拖拽裁剪框完毕时回调触发
onChange
:拖拽裁剪框移动时回调触发
本文主要用到onSelect回调,在该回调中可以拿到裁剪图片的尺寸、裁剪坐标信息,然后使用使用canvas的drawImage方法对canvas对象进行裁剪。
实现流程
1、上传控件监听用户选择上传文件的change事件,事件获取到File对象信息,并使用FileReader对象读取图片边展示,这个地方就需要使用File对象的一些信息来判断用户是否选择了图片文件等之类的边界判断;
2、将FileReader读取到的图片信息转换成base64编码的图片url并显示在页面中;
3、在base64编码的图片元素上初始化jcrop,监听jcrop的选中拖拽等事件触发canvas的drawImage方法;
4、设置一个按钮,用于用户拖拽裁剪框完毕之后的确认上传按钮,用户点击该按钮之后使用FormData对象自主读取canvas元素的二进制内容并构造一个表单,最后通过ajax发送该表单;
5、服务器端接收处理该表单数据跟古老的普通的文件上传一致,无需其他特别处理,只不过上传上来的图片是已经裁剪好的图片,无需服务器端再次处理。
在线示例:https://blog.jjonline.cn/demo/corp_image/index.html
参考资料:
1、https://wangdoc.com/javascript/bom/file.html
2、http://www.w3school.com.cn/tags/html_ref_canvas.asp
谢谢分享