【vue系列】图片裁剪插件 vue-cropper,源码解读

2,216 阅读5分钟

历史回顾:

前言

最近有个对证件照、二维码进行裁剪上传需求,去查资料,有很多帖子对 xyxiao001/vue-cropper 插件的使用介绍。就去github上找到它,快速把需求开发上线了。笔者主要是对 vue-cropper 插件进行了二次封装。封装之后包含了点击选择图片、对图片进行base64转化、裁剪、以及调用后台接口上传。

图片裁剪需求开发中的疑问

笔者在开发的过程中留下了几处疑问,比如:剪裁框是如何透过蒙层看到底层的原图片的?拖动图片、收缩图片跟图片裁剪之间是如何处理的等等。大致梳理出来有以下疑问。

  • 1.如何拖动图片,以及控制图片缩放的?
  • 2.裁剪框是如何透过蒙层显示图片的?
  • 3.如何生成截图框,截图的大小怎样控制?
  • 4.如何清除裁剪框?
  • 5.裁剪之后,如何生成图片呢?
  • 6.选择图片上传图片,并展示?
  • 7.为什么把图片转换为blob对象了?(todo)
  • 8.如何自动裁剪截图?
  • 9.手机端是如何控制手势缩放的?

vue-cropper 源码阅读

面对这些问题,要想知道答案,没有什么好办法,就是去读源码,边调试,边读源码。我的读源码习惯是,从源码的commit最初的提交开始看。其实有些疑问刚好是插件不断修复bug、扩展功能添加进去的。

1、如何拖动图片,以及控制图片缩放的?

针对这个问题,选了一个初始的commit版本,本地跑起来查看。

图片的拖动和缩放,是通过css3的 scaletranslate3d(x,y,z) 控制的,每次改变的都是图片的外层div盒子,缩放和移动的也是这个外层div,而不是img本身。

图片的拖动控制,并没有绑定到图片上,或者图片的父级盒子上,而是采用了 cropper-drag-box 一个空div。在这个div上绑定了鼠标事件和触摸事件。这个疑问就解决了,来看下一个。

2、裁剪框是如何透过蒙层显示图片的?

猛地一看,笔者还以为是剪裁框透过蒙层看到了原图。其实这里是在剪裁框里还有一份原图,用于显示剪裁的部分。好奇的是为什么原图,跟剪裁中的原图移动的位置有差。

于是笔者去扒源码,看到 'translate3d('+ (x - cropOffsertX) / scale + 'px,' 原来是 原图片的当前的top的水平位置 - 裁剪框的top水平位置,哈哈哈,原来如此 😂

3、如何生成截图框,截图的大小怎样控制?

当点击开始裁剪就监听裁剪事件,记录裁剪框的初始位置,截图移动,通过当前位置减去初始位置,得到剪裁框的宽高,动态属性更新到裁剪框div的style上就好了。 还是很简单的。

4、如何清除裁剪框?

这就更简单了,用脚丫想想就知道,重置裁剪框上的style就可以了。去看了眼源码,果然如此。

5、裁剪之后,如何生成图片呢?

裁剪之后,通过创建 canvas ,调用 drawImage 来绘制,再使用 canvas.toDataURL("image/jpeg", this.outputSize) 来生成base64图片。

6、选择图片上传图片,并展示?

做过上传图片的的肯定都知道。不过这里还是科普下吧。

FileReader 可以异步读取文件,通过 FileReader.readAsDataURL() 开始读取指定的 Blob 中的内容。当监听到上传完成,result 属性中将包含一个data: URL格式的 Base64字符串 以表示所读取文件的内容。

7、为什么把图片转换为blob对象了?(todo)

看插件的提交记录,有一次commit提交,就是将图片转换为 blob对象,但是没说为什么这么处理。剪裁前的图片展示,为什么把图片转换为blob对象了?原来的base64不行么?笔者给作者提交了一个评论,说了笔者的疑惑。还没得到回复。

这里没想明白?清楚的、理解的可以私聊我(微信:uxiaoxiaoxx)

8、如何自动裁剪截图?

当图片被异步读取完成,就初始化裁剪框,按照裁剪框的默认位置进行裁剪。

9、手机端是如何控制手势缩放的?

主要采用的移动端 touches 控制,如果 e.touches.length == 2,则为两个手指进行缩放操作。

它的具体实现是:oldL 为两个手指的初始直线距离,newL 为两个手指的终止直线距离, 按照 cha = ~~(newL - oldL) 的cha比例系数计算图片放大缩小的倍数 😁

扩展:

Math.sqrt() 函数返回一个数的平方根;

Math.pow() 函数返回基数(base)的指数(exponent)次幂

小结

日常业务需求开发,大部分的开发者就可以做,可是,做这样的事情,我们该怎么提升自己?让自己有所成长?首先我们能在需求开发中的深度思考,抛出疑问,然后通过阅读源码(或者重构封装),来解答自己的疑惑,这是自我提升的一种方式。希望我的学习方法,能带给你一点启示。共勉!最后,感谢xyxiao001对vue-cropper贡献。