简介
标注平台很关键的一点,对于整个图片为底图的画布,需要支持缩放、拖拽,并且无论画布位置在哪里,大小如何,所有绘制的点、线、面的坐标都是相对于图片左上角的,并且,拖拽、缩放,点、线、面的坐标不改变。
要实现这一点,其实就是理解这个画布的坐标系,以及变换矩阵。
画布
fabric.js的底层是canvas绘图。canvas作为画布,要支持拖拽、缩放,就需要有个容器监听鼠标事件。如果我们直接对canvas dom进行拖拽、缩放,有两个缺陷:1是后续只能在最新的canvas范围内进行缩放、拖拽,不易点击;2是dom大小和位置变更,会不断触发浏览器的重绘,性能开销较大,容易卡顿。
为了解决上述问题,我提供以下解决方案:
以一个固定位置的canvas作为画布,拖拽、缩放,只改变canvas的变形矩阵即可transform(a, b, c, d, e, f)。
a:水平方向的缩放;b:竖直方向的倾斜偏移;c:水平方向的倾斜偏移;d:竖直方向的缩放;e:水平方向的移动;f:竖直方向的移动
因此,我们的平移可以通过设置e和f,缩放可以通过设置a和d。
在fabric.js中,可以通过viewportTransform属性,获取这六个参数。然后再通过setViewportTransform方法,设置这六个参数。
居中
画布居中是标注平台必须的功能,因为你拖拽缩放后图像可能偏离视图范围很多,需要快速复原。居中的功能,说起来简单,但是实现还是有一些困难。其原理如下图所示。
首先,获取要标注的图片,宽100,高80,设置canvas的背景图片。
const canvas = new fabric.Canvas(domId, {
width: 100,
height: 80
});
canvas.setBackgroundImage('图片链接');
其次,初始化canvas的为父容器的宽高(100x60),初始化变换矩阵[1,0,0,1,0,0],即平移0,缩放1(不缩放)。
canvas.setDimensions({ width: 100, height: 60 });
canvas.setViewportTransform([1,0,0,1,0,0]);
再次,根据canvas父容器的宽高(100x60),按图像能全展示的比例对canvas视图范围进行等比缩放(0.75),得到新的宽高75x60。即a=0.75,d=0.75。
canvas.setCanvasZoom(0.75);
最后,将canvas视图范围往右平移15,即e=15,f=0。
canvas.setViewportTransform[0.75,0,0,0.75,15,0]
这样,我们的canvas始终是父容器的范围,而viewport范围已经居中。
注意,变换矩阵设置后,还需要执行渲染重置方法,这个是fabric的底层逻辑漏洞,如果不重置,画布上的点线面的位置会偏移。
canvas.renderAndReset();
平移
首先,我们需要监听canvas的mousedown、mousemove、mouseup。
mousedown的回调函数,我们设置全局变量active为true(用于mousemove判断是否需要执行平移),并记录lastX = e.pageX和lastY = e.pageY。
mousemove的回调函数,如果active为true,我们根据e.pageX - lastX和e.pageY - lastY确定横向和纵向的平移量,再通过对transform的e和f进行叠加这个平移量,实现平移功能。
mouseup的回调函数,设置acitve为false。
缩放
监听canvas的wheel,鼠标滚轮事件。
wheel的回调函数,如果e.deltaY > 0,则表示缩小,否则表示放大。缩小,我们对当前的缩放比例统一进行 -0.1,放大统一 +0.1。如果你想更人性化,可以对缩放比例做非线性的变化。
注意点
canvas的缩放,是基于canvas左上角的原点,并非基于平移后的点为原点。因此,需要先将canvas的平移参数置为0后,再设置缩放比例。
另外,我们的缩放,是基于鼠标当前位置为原点进行缩放。如图,红色点处(x, y)进行缩放,视图从v1变到v2,我们已知两个视图对应的缩放比例z1和z2,要求deltaX和deltaY,根据相似定理得:
deltaX = (x - left) * (1 - z2 / z1)
deltaY = (y - top) * (1 - z2 / z1)
newLeft = deltaX + left
newTop = deltaY + top
因此,我们在上一步设置好缩放比例后,执行平移操作(e=newLeft,f=newTop),从而将视图从黑色框缩放至蓝色框。
预告
下一章, 聊聊fabric的几何定制化渲染。