大家好,我是前端西瓜哥。
最近有读者咨询我一个缩放问题。
应该是他要给项目做缩放功能,然后看到了 我图形编辑器缩放的文章,想要跟着我文章的思路移植到他的项目上。
但他搞不定,来找我。我给他搞定了,然后就有了这篇文章。
他给了我一个最小实现 demo。
我看了下,他用的是 zrender 渲染器(ECharts 的底层渲染器)。
和我之前写的文章不同的是,他用了 zrender 提供的 group 元素,给它设置了 xy 和 scale。然后绘制的元素都放在这个容器元素下。
我之前写的文章呢,没有这个 group 的概念,是给所有的坐标去乘一个视图矩阵,做坐标系的转换,能够正确落在适口矩形的新的位置。
这个 group 就有点像视口,虽然底层思路是一样的,但实现细节有很大不同。
首先是拖拽移动画布的逻辑,变得简单了,直接 dx dy 加到 goup 的 x y 上就行了,不用除以 scale。
画布坐标是缩放后坐标,转为 group 下的坐标,要先减去 group.x 或 group.y,然后除以 scale,得到缩放前的坐标。
const xInGroup = (x - group.x) / scale;
const yInGroup = (y - group.y) / scale;
然后是重点,缩放逻辑。
和我之前写的文章一样,要点还是:你原来在 group 的哪个相对位置进行缩放的,缩放后也得在那个位置。
你要改 group 的 xy。
打个比方,假设这个点原来在相对 group 宽和高的四分之一处,假设 group 的 x y 是 (0, 0),宽高是 12 和 20,那点坐标就是 (3, 5)。
先不改变 group 的 xy,缩放为原来的 2 倍,点跑到了哪里?(6, 10)。
我们要让它表现为是以这个点进行缩放的,那我们就得把这个点移动对上原来的位置,于是让 group 的 x y 分别移动 -3 -5。
上面动图左上角矩形宽高就是要求的相对位移 dx 和 dy。
把图画出来,就好理解了。
我们要求的是这个图中的绿色向量。
列个式子算一下:
const dx
= -(scale / prevScale * (cx -group.x) - (cx - group.x))
= -((cx - group.x) * (scale / prevScale - 1))
= (cx - group.x) * (1 - scale / prevScale)
// dy 同理
const dy
= (cy - group.y) * (1 - scale / prevScale)
代码:
zr.on("mousewheel", function (e) {
const prevScale = scale;
scale < 0.2 ? (scale = 0.2) : scale;
scale += e.wheelDelta > 0 ? 0.1 : -0.1;
const dx = (e.offsetX - group.x) * (1 - scale / prevScale);
const dy = (e.offsetY - group.y) * (1 - scale / prevScale);
group.attr({
scaleX: scale,
scaleY: scale,
x: group.x + dx,
y: group.y + dy
});
});
演示:
线上示例 demo:
https://codesandbox.io/s/vm8fh4?file=/src/index.ts
这位读者他用了特有的 origin 属性。这个 origin 可以用来指定 group 的缩放中心。
如果用 origin,你还是要改 xy 的,跑不了,别想太多。不仅如此,逻辑还更复杂了,毕竟又引入了新事物。不建议用。
如果你用 svg 方案,也是同理。
这里给一个 svg.js 的缩放在线示例:
https://codesandbox.io/s/vsylk4?file=/src/index.ts
缩放的要点在于,两个坐标系转换关系,要多画图推导推导。
我是前端西瓜哥,关注我,学习更多前端可视化和图形编辑器知识。