零、 介绍
本篇文章主要介绍如何从零开始做一个完整的图片裁剪组件
背景
一个图片裁剪组件的应用场景其实比较多,相应的第三方插件也不少,但有时候会需要一些特定的功能,比如想有个特定样式的裁剪框,想批量裁剪,甚至想直接裁出定制化的尺寸等等,这时就只能手写一个裁剪组件了。
大致流程
一、上传读取图片
上传图片时,用onChange事件来获取该file对象,里面会包含文件的name,size,type,和修改时间等信息(只读),预览图片之前可以通过这些信息来限制上传图片的格式、类型等等。
handleChange = (e) => { const files = Array.from(e.target.files); if (!files.length) { // 释放上传系统存储当前值,避免相同文件不触发onchange事件 this.imageUpload.value = null; return; } // 上传规则校验(比如图片格式,图片大小限制等等 .... } render() { return ( <div> <input type="file" onChange={this.handleChange} // 监听上传事件 multiple="true" // 是否批量上传 accept="image/*" // 控制上传文件的类型,image/*表示接收所有image后缀的文件 ref={e => { this.imageUpload = e; }} /> </div> ) }
⚠️注意点: 使用onChange上传文件时,如果连续两次选择相同的文件,第二次会因为value还是同一个值导致onChange不会触发,所以在第一次上传完之后,需要将input的value置为空。
二、Canvas绘制图片
2.1 解析图片信息
利用我们刚刚获取的file文件对象,可以解析出一些图片的关键信息,比如图片的宽、高以及最重要的base64。这里我们主要是通过FileReader.readAsDataURL来实现。
触发 FileReader.onload 方法时,会返回一个基于 base64 编码的 data-uri 对象。
// 读取图片原始信息方法 filesInfo = (file) => { return new Promise((res, rej) => { let reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e) { // 实例一个Image对象,为了获取宽、高(下文预览图片时需要) let image = new Image(); image.onload = function() { res({ width: image.width, // 宽 height: image.height, // 高 // 其他图片信息 // ... }); }; image.src = e.target.result; // base64 image.crossOrigin = \'Anonymous\'; //解决跨域问题 }; }); },
其实除了上述还有第二种方式, window.URL.createObjectURL,有兴趣的小伙伴可以自行查阅一下,本文就不再做赘述了。
2.2 预览图片
canvas的宽高分为2种:
-
canvas style样式中的宽高:是整个canvas的宽高,决定了整个canvas context的大小;
-
canvas元素属性的宽高:表示canvas的画布大小。
因此我们的自适应图片居中策略:
⚠️关于设备像素比具体可以戳