@司马淇(simaqi)

Element 定义: 在 G2 中,我们会将数据通过图形语法映射成不同的图形,比如点图,数据集中的每条数据会对应一个点,柱状图每条数据对应一个柱子,线图则是一组数据对应一条折线,Element 即一条/一组数据对应的图形元素,它代表一条数据或者一个数据集,在图形层面,它可以是单个 Shape 也可以是多个 Shape,我们称之为图形元素。

image.png
Element && Shape 类图

ELement

  1. class Element extends Group {
  2. /** 唯一 id 标识 */
  3. id: string;
  4. /** element 原始数据 */
  5. data: object;
  6. /** 绘制数据 */
  7. mappingData: object;
  8. /** 绘制的 shape 类型 */
  9. shapeType: string;
  10. private _shapeFactory;
  11. constructor(cfg) {
  12. this._shapeFactory.drawShape(this.shapeType, model, this); // 绘制图形
  13. }
  14. /** 设置 shape 的样式 */
  15. style(cfg: object): {
  16. this._shapeFactory.updateShape(this.shapeType, cfg);
  17. }
  18. /** 设置状态 active selected inactive */
  19. setState(stateName: string, stateStatus: boolean) {
  20. // 进行逻辑判断,符合规则就调用 this.shapeFactory.setState();
  21. }
  22. /** 更新 Element,当上游数据发生变化的时候 */
  23. update() {}
  24. /** element 主动更新数据时,需要通知上游进行数据更新 */
  25. updateData() {}
  26. /** 销毁 */
  27. destroy() {}
  28. /** 清除状态 */
  29. clearState(stateName?: string) {}
  30. /** 判断是否存在某个状态 */
  31. hasState(stateName: string): boolean {}
  32. /** 获取 element 对应的原始数据 */
  33. getData(): object {}
  34. /** 获取 element 对应的绘制数据 */
  35. getMappingData(): object {}
  36. }

Element 主要职责:

  • 创建 shape 图形
  • update() element 进行更新,数据更新机制需要
  • style() 设置 shape 样式,交互行为使用
  • setState() 设置状态,交互行为使用
  • updateData() element 自身进行数据更新,这时候需要触发 view 的数据更新机制,交互行为使用
  • destroy() 销毁,需要通知 Shape,动画需要

对于样式以及 state 的更新,都由具体的 Shape 进行执行。

状态管理

目前提供三种图形状态:active、selected 以及 inactive(🤔还是取名 dark 待议),对于 Element 来说这三种状态都是独立的,即用户开启了 active 状态之后又开启了 selected 状态,element 就会在 active 的状态上叠加 selected 状态,交互状态之间的关联全部交由具体的交互行为负责,所以我们除了提供 setState() 方法外,还会提供 clearState() hasState() 方法。

数据更新

交互行为中,经常会有更新单个 element 数据的需求,所以我们在 element 上提供了 update() 方法,以支持数据的更新。update() 方法被调用后,element 需要通知上游 view 进行数据更新操作,这个时候就需要一种机制来保证,同时希望不要同 View 进行耦合…

Designing…

Shape

Shape 采用工厂设计模式,但是这里利用了 JS 的灵活性,并没有采用纯面向对象的方式实现,而是使用对象代替类实现,通过属性的覆盖来代替对父类方法的覆写。

  • 接口定义:(方法的入参以实际代码为准) ```typescript /**
    • ShapeFactory / interface ShapeFactoryCFG { /** shape 主题 / _theme: DataPointType; / 坐标系对象 */ _coord: Coord; name?: string; / 默认生产的 shape 类型 / defaultShapeType: string; /** 返回 shape 绘制默认的关键点 / getDefaultPoints?: (pointInfo: ShapePointInfo) => PointObject[]; / 创建具体的 G.Shape 实例 */ drawShape?: (type: string | string[], cfg: ShapeDrawCFG, container: Group) => Shape; / 设置坐标系 / setCoord: (coord: Coord) => void; /** 设置主题 / setTheme: (theme: DataPointType) => void; / 根据名称获取具体的 shape 对象 */ getShape: (type: string | string[]) => ShapeCFG; / 获取构成 shape 的关键点 / getShapePoints: (type: string | string[], pointInfo: ShapePointInfo) => PointObject[]; /** 设置状态量 / setState: (type: string | string[], stateName: string, stateStatus: boolean) => void; / 更新 Shape 样式 */ updateShape: (type: string | string[], attrs: object) => void; / 获取 Shape 对应的 marker / getMarker: (type: string | string[]) => G.Shape | G.Group; /** 销毁 / destroy: (type: string | string[]) => void; }

/**

  • Shape / interface Shape { /** shape 名称 / name?: string; / shape 对应 Geometry 的类型 */ geometryName?: string; _coord: Coord; getCoord: () => Coord; / 0~1 path 转 画布 path / parsePath: (path: string, islineToArc: boolean) => any[]; /** 0~1 point 转 画布 point / parsePoint: (point: PointObject) => PointObject; /* 0~1 points 转 画布 points / parsePoints: (points: PointObject[]) => PointObject[];

    draw: (cfg: ShapeDrawCFG, container: Group) => Shape; / 计算绘制需要的关键点,在注册具体的 shape 时由开发者自己定义 */ getPoints?: (pointInfo: ShapePointInfo) => PointObject[]; / 响应状态量 / setState: (stateName: string, stateStatus: boolean) => void; /** 更新 shape / update: (attrs) => void; / 获取对应状态的样式 */ getStateStyle: (stateName: string) => object; / 获取 shape 对应的动画配置 / getAnimateCfg: (animateType: string) => object; /** 获取 shape 对应的 marker / getMarker: () => G.Shape | G.Group; /* 销毁 / destroy: () => void; }

  1. PS `registerShape()` 逻辑以及对外的接口仍同 3.x 保持一致。
  2. Shape 职责:
  3. 1. 定义图形的关键点,即顶点
  4. 1. 绘制,即创建具体的 G.Shape/G.Group
  5. 1. 响应状态量
  6. 1. 更新图形样式
  7. 1. 动画
  8. <a name="1uDWy"></a>
  9. ### 响应状态量
  10. 1. 目前开放三个状态量: 'active''selected''inactive'
  11. 1. 状态量对应的样式设置:
  12. 3.6 版本之前,通过 `getActiveCfg()` `getSelectedCfg()` 方法进行设置,但是由于那时并没有对图形状态进行设计和区分,所以这个接口基本没有多少使用,即便是默认提供的图形,而且这两个函数的命名也不是很准确。而在 3.6eva 项目中改版的 G2)中,开放了三种方式来支持状态对应的样式设置:
  13. - 在自定义 Shape 中通过 `getActiveStyle()` `getSelectedStyle()` 以及 `getInactiveStyle()` 接口进行设置
  14. - 在主题上为各个默认注册的 shape 开发了不同状态下的样式配置支持:
  15. ```typescript
  16. // console.log(G2.Global.theme.shape);
  17. shape: {
  18. geometryName: { // 几何标记类型
  19. shapeName: { // 具体的 shape 名称
  20. default: {}, // 默认的样式
  21. active: {} || (shapeStyle) => {}, // active 状态下的样式,支持回调函数
  22. inactive: {} || (shapeStyle) => {}, // inactive 状态下的样式,支持回调函数
  23. selected: {} || (shapeStyle) => {}, // selected 状态下的样式,支持回调函数
  24. }
  25. },
  26. },
  • <GeometryType>().active().selected().inactive() 方法进行配置,同时这些方法还可以控制是否允许 shape 被 active、selected 或者 inactive

4.0 方案
因为 4.0 的设计中,无论是 Element 还是 Shape 都只是提供 setState() 接口由外部调用,即只负责对这些状态量进行反馈,所有的逻辑都是由调用方(交互行为)去控制,所以之前 <GeometryType>().active().selected().inactive() 接口决定废弃。而对于状态量样式的设置提供两种方法:

  1. getStateStyle() 方法,为自定义 Shape 提供自定义样式接口,后续新增状态量也不需要新增方法了
  1. /**
  2. * 默认会去 theme 中取 stateName 对应的样式
  3. * 用户也可以在自定义 Shape 中覆写该方法
  4. */
  5. getStateStyle(stateName: string) {}
  1. 主题上也支持为各个 shape 配置不同状态下的样式
  1. // console.log(G2.Global.theme.shape);
  2. shape: {
  3. geometryName: { // 几何标记类型
  4. shapeName: { // 具体的 shape 名称
  5. default: {}, // 默认的样式
  6. active: {} || (shapeStyle) => {}, // active 状态下的样式,支持回调函数
  7. inactive: {} || (shapeStyle) => {}, // inactive 状态下的样式,支持回调函数
  8. selected: {} || (shapeStyle) => {}, // selected 状态下的样式,支持回调函数
  9. }
  10. },
  11. },

动画配置

引入数据更新机制后,无论组件还是 Shape 都可以各自负责自己的动画了。所以 Shape 可以在 draw()、update() 等方法中直接编辑动画,但是考虑到用户还会有自定义动画的需求,所以还会在 Shape 基类中提供 getAnimateCfg(type: string) 的接口,同状态量样式配置一样,用户还可以在主题上对动画进行配置:

同 3.x,动画分为四种类型:appear enter update leave

  1. /**
  2. * G2.Global.theme.shape 在主题中配置动画
  3. */
  4. shape: {
  5. geometryName: { // 几何标记类型
  6. shapeName: { // 具体的 shape 名称
  7. default: {}, // 默认的样式
  8. active: {} || (shapeStyle) => {}, // active 状态下的样式,支持回调函数
  9. inactive: {} || (shapeStyle) => {}, // inactive 状态下的样式,支持回调函数
  10. selected: {} || (shapeStyle) => {}, // selected 状态下的样式,支持回调函数
  11. animate: {
  12. appear: {
  13. animation: string | (shape, animateCfg, coord) => {}, // 动画执行函数,默认我们会提供几个
  14. easing: 'easeQuadIn', // 动画缓动效果
  15. delay: 100, // 动画延迟执行时间
  16. duration: 600 // 动画执行时间
  17. },
  18. enter: {} | false,
  19. update: {} | false,
  20. leave: {} | false,
  21. }
  22. }
  23. },
  24. },
  25. /**
  26. * Shape.getAnimateCfg(type)
  27. */
  28. getAnimateCfg(type: string) {
  29. // 默认会从主题中获取对应的动画配置
  30. // 在自定义 Shape 中用户也可以复写该方法,也可以直接在主题上进行配置
  31. }