前言
当初接触到rxjs 这套以流的方式处理事件及资料的库时,觉得非常新奇,便尝试自己从头写了一个rxjs + vuerx + vue 支援触控端、响应式的图片剪裁元件,取名叫vuejs -clipper 并发布至npm。
算是一个简单的元件,这篇主要介绍下当初的一些有趣的思考方向,而非程式方面如何实作。
完整的程式码、使用範例都在连结里,有兴趣、喜欢的话可以使用看看或给个星当做鼓励 :)
vuejs-clipper 展示
展示源码npm结构
前端使用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 并没有要做图片缩放,并想把这个元件做成像一个 元素,根据图片自动调整比例,但是只做了根据宽度调整高度,而没有设定高度来调整宽度的选项。 。 。
这就造成一个问题,假设图片剪裁元件设定 width: 500px,当上传一个很长的图片时,网页的布置会被拉的很长。
算是设计上的疏失,可能的解决办法写在这里。
clipper-fixed 的话是有设定剪裁比例的,长宽比则是看剪裁比例相同。
缩放图片
clipper-fixed 缩放图片,绑定滚轮/两指触控事件,会发现如果使用固定比例缩放(假设每次缩放0.1倍好了),在图片较小时会觉得缩放的很快,图片很大时缩放则感觉非常缓慢。
因此需要用线性方式来调整缩放比例:
(下图只是示例,非真正的比例)
缩放率要随着图片的大小增加,公式的话…一元、二元应该都是可以的,调整到自己感觉最舒适的参数。
输入和输出
想要表现的像一个,因此有src属性,接受一个图片的 URL。输出则是一个canvas element,使用者可将canvas绘製到其他地方,或者转成image元素、Blob、URL等,这些操作是比较消耗效能和时间的。
设计这个元件(而非函式库)就是想要间单一点,单纯控制UI,不介入一些上传、转换图片的处理。
结语
总结一下这次作这个元件可以改善的地方,以后(应该是不会有以后.w.)可以改进:
剪裁元件的长宽设定方式不佳剪裁框缩放基準点可以设为八个剪裁元件的初始状态无法设定 (一开始剪裁框的位置、大小等)。个人认为 clipper-fixed 这种操作方式是最通用也最没毛病的,但我也不懂一些UI/UX啥的,不知道怎样使用者操作比较顺手。 。 。
虽然说是用 vue + vuerx + rxjs 但本文几乎没有讲到这三者的时作和程式码,因本人也算是刚接触的生手,尚在摸索,就没有对这几个库作讲解。
就先介绍到这,希望大家会喜欢。