通过相机实现画布级操作 #4344
xiaoiver posted onGitHub
之前 G6 支持以下画布 / 视口操作: https://g6.antv.antgroup.com/api/graph-func/transform
在 G 5.0 中可以通过相机实现: https://g.antv.antgroup.com/api/camera/intro
不妨先看看其他库的相机 API 是如何设计的。
Sigma.js
在 Sigma.js 中提供了相机功能,使用方式如下:
const renderer = new Sigma(graph, container, {
minCameraRatio: 0.1,
maxCameraRatio: 10,
});
// 获取相机
const camera = renderer.getCamera();
// 带过渡动画效果的平移
camera.animate({ x: 100, y: 100 }, { duration: 600 });
// 带过渡动画效果的旋转
camera.animate({ angle: 30 }, { duration: 600 });
// 带过渡动画效果的 zoomIn/Out
camera.animatedZoom({ duration: 600 });
camera.animatedUnzoom({ duration: 600 });
camera.animatedReset({ duration: 600 });
以上方法底层调用的都是一个通用的 animate
方法,其签名如下:
animate(state: Partial<CameraState>, opts?: Partial<AnimateOptions>, callback?: () => void): void
其中相机状态 CameraState
包含位置、旋转角度和缩放等级:
https://github.com/jacomyal/sigma.js/blob/main/src/types.ts#L24-L32
export interface Coordinates {
x: number;
y: number;
}
export interface CameraState extends Coordinates {
angle: number;
ratio: number;
}
因此可以组合相机变换。
mapbox
Mapbox 提供了相机能力: https://docs.mapbox.com/mapbox-gl-js/api/map/#instance-members-camera
其中包括:
- panBy / panTo 平移相对距离 / 到指定点
- zoomTo 缩放到指定等级
- rotateTo
map.rotateTo(30, {duration: 2000});
- fitBounds 类似 fitView
- stop 停止相机动画
G6 5.0
通过相机动作可以实现一系列画布 / 视口级的操作: https://g.antv.antgroup.com/api/camera/action
平移
首先需要明确在哪个坐标系下平移。4.0 选择了视口坐标系,其实是非常合理的,因为如果发生了 zoom,世界坐标系下的移动常常让用户迷惑,比如高缩放等级下,在视口坐标系下移动会更加直观。
在 4.0 中提供了平移相对距离和绝对坐标两种方式: https://g6.antv.antgroup.com/api/graph-func/transform#graphtranslatedx-dy-animate-animatecfg
// 平移相对距离
graph.translate(100, 100);
graph.translate(100, 100, true, {
duration: 100, // 带动画
});
// 平移到绝对坐标
graph.moveTo(200, 300);
graph.moveTo(200, 300, true, {
duration: 100, // 带动画
});
第三个参数可以去掉,传了动画配置就认为需要执行过渡动画,否则不传即可。 在内部实现时,可以通过相机的 pan 动作实现,但需要注意的是相机移动方向和视觉上的视口相反,例如相机向左移画面中的对象整体向右移动。
因此我们提供 translate / translateTo 两个方法,异步用于感知相机动画何时结束:
await graph.translate(200, 300);
await graph.translateTo({ x: 200, y: 300 }, {
duration: 2000,
easing: 'linear',
});
为何不使用 move / moveTo,一方面是为了和 CSS Transform 命名保持一致,另一方面也是和新增的 “组合变换” 保持统一。
旋转
G6 4.0 未提供。我们可以提供 rotate 相机动作,同样支持旋转一个相对角度和旋转到绝对值两种方式,逆时针方向: https://g.antv.antgroup.com/api/camera/action#rotate
await graph.rotate(30, { x: 250, y: 250 }, { duration: 1000 });
await graph.rotateTo(120, { x: 250, y: 250 }, { duration: 1000 });
缩放
- 以相机在世界坐标系下的位置为中心进行缩放 https://g.antv.antgroup.com/api/camera/params#setzoom
- 固定视点,即以视口坐标系下的点为中心进行缩放 https://g.antv.antgroup.com/api/camera/params#setzoombyviewportpoint
和 4.0 保持一致,第一个参数为缩放比例(相对 or 绝对),第二个参数可选为缩放中心点(默认为当前视口中心点),第三个参数可选为动画相关配置:
// 缩放相对比例
graph.zoom(
0.5,
{ x: 250, y: 250 },
);
// 缩放到绝对值
await graph.zoomTo(
1,
{ x: 250, y: 250 },
{
duration: 1000,
},
);
需要注意的是和 scale / scaleTo 完全不一样,后者会实际改变画布中图形的大小,而 zoom / zoomTo 仍是一种相机行为。
组合变换
如果我们想同时进行平移、旋转和 zoom 变换,可以使用通用的 transform
方法,并且也可以带上过渡动画,这一点在 4.0 中也是做不到的。其中 translate
rotate
和 zoom
与上面的方法名保持一致,对于旋转和缩放中心使用 origin
属性:
await graph.transform({
translate: {
dx: 100,
dy: 100,
},
rotate: {
angle: 30
},
zoom: {
ratio: 1.2
},
origin: {
x: 100,
y: 100,
}
}, {
duration: 1000,
});
fitCenter
让相机聚焦到当前 Graph 的中心,可以带过渡动画:
await graph.fitCenter();
await graph.fitCenter({
duration: 2000,
easing: 'ease-in',
});
获取 Graph 包围盒中心后,调用 translateTo 完成。
focusItem
让相机聚焦到某一个 item,可以带过渡动画:
await graph.focusItem('Argentina');
await graph.focusItem('Argentina', {
duration: 2000,
easing: 'ease-in',
});
支持通过 Node / Edge 的 ID 查询,定位到 item 在 Canvas 坐标系下的位置后,调用 translateTo 完成。
fitView
类似 mapbox 的 fitBounds。
和 4.0 的小区别是将 padding 和 rules 合并成一个参数对象,避免这样的写法:graph.fitView(undefined, undefined, {})
await graph.fitView(
{
padding: [150, 100],
rules: {
direction: 'both',
ratioRule: 'min',
},
},
{
duration: 1000,
easing: 'ease-in',
},
);
stopTransition
在带动画效果的变换过程中,随时可调用该方法终止:
graph.translate(100, 100, { duration: 1000 });
graph.stopTransition();