Vue 图片剪裁元件设计思考

1,833 阅读6分钟

前言

当初接触到 rxjs 这套以流的方式处理事件及资料的库时,觉得非常新奇,便尝试自己从头写了一个 rxjs + vuerx + vue 支援触控端、响应式的图片剪裁元件,取名叫 vuejs-clipper 并发布至 npm。

算是一个简单的元件,这篇主要介绍下当初的一些有趣的思考方向,而非程式方面如何实作。

完整的程式码、使用范例都在连结里,有兴趣、喜欢的话可以使用看看或给个星当做鼓励 :)

vuejs-clipper 展示

结构

前端使用Vue,要在Vue Component中使用rxjs的话,vuerx这个Vue的插件(Plugin)提供两者很好的结合,让Vue component多了个subscriptions选项可定义 rxjs 的 subject 和 Observable 等。

以及提供v-stream,和v-on一样监听Dom事件,但是以rxjs Observable流式来处理。

元件使用Vue SFC方式开发,最后用@vue/cli 打包成umd挡案可用script引入。

构想

(以下图多注意)

其实已经有许多图片剪裁元件的库了,像是这个cropper.js,功能应该非常强大齐全(没有使用过...汗颜),还有结合cropper.js和Vue component的vue-cropperjs等等......

这边打个岔,先来定义一些名词以方便我后面继续讲下去。。。

这张图以vuejs-clipper为例

如果刚刚有玩一下cropper.js的话可以发现,他的剪裁匡和图片都是可以缩放移动的。

不过当初我的想法是,其中一个往左移动,不就相对于另一个元素往右移动了;其中一个元素缩小,就相对于另一个元素放大了啊。所以我认为图片或剪裁匡,选择其中一个进行缩放和移动就好。

基于这个原则我做了两种不同的元件,取名叫 clipper-basic(示例) 和 clipper-fixed(示例)。

clipper-basic

手指/滑鼠在剪裁元件上只能缩放移动剪裁匡,但可以另外绑定属性来控制图片旋转和放大。

clipper-fixed

手指/滑鼠在剪裁元件上只能缩放移动图片,但可以另外绑定属性来控制图片旋转。

元件设计

接下来介绍一些设计、优化的重点。

位置、大小使用百分比

剪裁匡、图片都是剪裁元件的子元素,长宽(width, height)、位置(top/bottom, left/right),需要用相对外匡的百分比来表示,否则荧幕大小一变就会跑版。

如果能使用 transform: translate 来替代layout: top/bottom, left/right 的话效能会更好,但当初开始写的时后我剪裁匡的位置和长宽并非一起计算,render的顺序不一致导致我用 translate 的话常常会跑版,后来就干脆用 top/bottom, left/right 来表示位置了。。。

拖弋剪裁匡

如何实作拖弋剪裁匡相信大家都会的:

红点是一开始点击的位置(基准点,跟着蓝色匡移动)

按着滑鼠/手指从P1移动到P2

移动匡(蓝色匡)移动后位置(以top, left表示的话):top 即是 y2 - y1,left 则是 x2 - x1

当然我们还要判断蓝色匡不能超出黑色外匡, 计算出来的 top/left 需要介于 min top/left 和 max top/left 之间。

上图演示一个例子,当鼠标移动到 蓝点1 时蓝色匡并不会移动因为已经抵达边界,但如果使用者依然按着滑鼠/手指并移回 蓝点2,会发现蓝色匡还是没有移动,这是因为基准点 红点 并没有改变,造成在灰色斜线的区域移动滑鼠,蓝色匡都不会移动,是一个不太好的体验。

解决方法:当蓝色匡抵达边界,但滑鼠/手指仍在移动时,基准点跟着改变且不超出边界,这样就不会有滑鼠白白移动的感觉。

以下示范滑鼠从P1点击移动到P2,P3,P4,红色基准点跟着改变,这样当鼠标一往回移动蓝色匡就可以跟着移动。

拖弋图片的话就比较没有这个问题,因为图片是可以超出边界的。

缩放剪裁匡

缩放剪裁匡其实就是判断一开始滑鼠点在匡上的哪个方位,就使用对秤点当作基准点,剪裁匡以该点不动来缩放。

可以发现Cropper.js是有八个基准点可以缩放的:

但当初的 clipper-basic 设计成剪裁匡是可以缩放成任意比例的,因此边上的基准点就不需要了,只做了四个基准点:

其实在任意比例 (长宽比例不限 ) 下这样设计是没问题的,但后来想要加上限制比例的设定就比较尴尬了,可以去展示把 ratio 选项打勾并缩放剪裁匡试试,因为少了边上的基准点,在水平或垂直缩放时用起来有些奇怪,但是对角缩放就不会。

剪裁元件长宽

正常来说图片剪裁元件可以设定外观长宽,但一开始 clipper-basic 并没有要做图片缩放,并想把这个元件做成像一个 <img> 元素,根据图片自动调整比例,但是只做了根据宽度调整高度,而没有设定高度来调整宽度的选项。。。

这就造成一个问题,假设图片剪裁元件设定 width: 500px,当上传一个很长的图片时,网页的布置会被拉的很长。

算是设计上的疏失,可能的解决办法写在这里

clipper-fixed 的话是有设定剪裁比例的,长宽比则是看剪裁比例相同。

缩放图片

clipper-fixed 缩放图片,绑定滚轮/两指触控事件,会发现如果使用固定比例缩放(假设每次缩放0.1倍好了),在图片较小时会觉得缩放的很快,图片很大时缩放则感觉非常缓慢。

因此需要用线性方式来调整缩放比例: (下图只是示例,非真正的比例)

缩放率要随着图片的大小增加,公式的话…一元、二元应该都是可以的,调整到自己感觉最舒适的参数。

输入和输出

想要表现的像一个<img>,因此有src属性,接受一个图片的 URL。 输出则是一个canvas element,使用者可将canvas绘制到其他地方,或者转成image元素、Blob、URL等,这些操作是比较消耗效能和时间的。

设计这个元件(而非函式库)就是想要间单一点,单纯控制UI,不介入一些上传、转换图片的处理。

结语

总结一下这次作这个元件可以改善的地方,以后(应该是不会有以后.w.)可以改进:

  • 剪裁元件的长宽设定方式不佳
  • 剪裁匡缩放基准点可以设为八个
  • 剪裁元件的初始状态无法设定 (一开始剪裁匡的位置、大小等)。

个人认为 clipper-fixed 这种操作方式是最通用也最没毛病的,但我也不懂一些UI/UX啥的,不知道怎样使用者操作比较顺手。。。

虽然说是用 vue + vuerx + rxjs 但本文几乎没有讲到这三者的时作和程式码,因本人也算是刚接触的生手,尚在摸索,就没有对这几个库作讲解。

就先介绍到这,希望大家会喜欢。