antvis/G6

通过相机实现画布级操作 #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 中提供了相机功能,使用方式如下:

https://github.com/jacomyal/sigma.js/blob/7b3a5ead355f7c54449002e6909a9af2eecae6db/examples/load-gexf-file/index.ts#L27-L42

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

其中包括:

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 });

缩放

和 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 rotatezoom 与上面的方法名保持一致,对于旋转和缩放中心使用 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 完成。

Mar-09-2023 18-19-09

focusItem

让相机聚焦到某一个 item,可以带过渡动画:

await graph.focusItem('Argentina');
await graph.focusItem('Argentina', {
  duration: 2000,
  easing: 'ease-in',
});

支持通过 Node / Edge 的 ID 查询,定位到 item 在 Canvas 坐标系下的位置后,调用 translateTo 完成。

Mar-09-2023 18-14-54

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',
  },
);

Mar-10-2023 17-03-47

stopTransition

在带动画效果的变换过程中,随时可调用该方法终止:

graph.translate(100, 100, { duration: 1000 });
graph.stopTransition();

类似:https://docs.mapbox.com/mapbox-gl-js/api/map/#map#stop


Fund this Issue

$0.00
Funded

Pull requests