feat: add registergraph for mobile

This commit is contained in:
AlbertAZ1992 2021-03-22 17:50:16 +08:00 committed by Yanyan Wang
parent d12f7c94f9
commit 14f15fedf9
4 changed files with 0 additions and 617 deletions

View File

@ -1,604 +0,0 @@
// @ts-nocheck
import { Point } from '@antv/g-base';
import Hierarchy from '@antv/hierarchy';
import { each, isString } from '@antv/util';
import {
GraphData,
Item,
NodeConfig,
ShapeStyle,
TreeGraphData,
GraphOptions,
} from '@antv/g6-core';
import { ITreeGraph } from '../../interface/graph';
import { getExtender } from '../../util/extend';
function treeGraphExtender(option: any, G6: { Graph?: any; Util?: any; }) {
const { Graph, Util } = G6;
const { radialLayout, traverseTree } = Util;
class TreeGraph extends Graph implements ITreeGraph {
private layoutAnimating: boolean;
constructor(cfg: GraphOptions) {
super(cfg);
this.layoutAnimating = false;
// 用于缓存动画结束后需要删除的节点
this.set('removeList', []);
this.set('layoutMethod', this.getLayout());
}
/**
* Layout
*/
private getLayout() {
const layout = this.get('layout');
if (!layout) {
return null;
}
if (typeof layout === 'function') {
return layout;
}
if (!layout.type) {
layout.type = 'dendrogram';
}
if (!layout.direction) {
layout.direction = 'TB';
}
if (layout.radial) {
return (data: any) => {
const layoutData = Hierarchy[layout.type](data, layout);
radialLayout(layoutData);
return layoutData;
};
}
return (data: any) => Hierarchy[layout.type](data, layout);
}
/**
*
* @param children
* @param child Item
*/
private static indexOfChild(children: TreeGraphData[], id: string): number {
let index = -1;
// eslint-disable-next-line consistent-return
each(children, (former, i) => {
if (id === former.id) {
index = i;
return false;
}
});
return index;
}
public getDefaultCfg(): any {
const cfg = super.getDefaultCfg();
// 树图默认打开动画
cfg.animate = true;
return cfg;
}
/**
* 🌲
* @param treeData
* @param parent
* @param animate
*/
private innerAddChild(treeData: TreeGraphData, parent: Item | undefined, animate: boolean): Item {
const self = this;
const model = treeData.data;
if (model) {
// model 中应存储真实的数据,特别是真实的 children
model.x = treeData.x;
model.y = treeData.y;
model.depth = treeData.depth;
}
const node = self.addItem('node', model!, false);
if (parent) {
node.set('parent', parent);
if (animate) {
const origin = parent.get('originAttrs');
if (origin) {
node.set('originAttrs', origin);
} else {
const parentModel = parent.getModel();
node.set('originAttrs', {
x: parentModel.x,
y: parentModel.y,
});
}
}
const childrenList = parent.get('children');
if (!childrenList) {
parent.set('children', [node]);
} else {
childrenList.push(node);
}
self.addItem(
'edge',
{
source: parent,
target: node,
id: `${parent.get('id')}:${node.get('id')}`,
},
false,
);
}
// 渲染到视图上应参考布局的children, 避免多绘制了收起的节点
each(treeData.children || [], (child) => {
self.innerAddChild(child, node, animate);
});
self.emit('afteraddchild', { item: node, parent });
return node;
}
/**
*
* @param data
* @param parent
* @param animate
*/
private innerUpdateChild(data: TreeGraphData, parent: Item | undefined, animate: boolean) {
const self = this;
const current = self.findById(data.id);
// 若子树不存在,整体添加即可
if (!current) {
self.innerAddChild(data, parent, animate);
return;
}
// 更新新节点下所有子节点
each(data.children || [], (child: TreeGraphData) => {
self.innerUpdateChild(child, current, animate);
});
// 用现在节点的children实例来删除移除的子节点
const children = current.get('children');
if (children) {
const len = children.length;
if (len > 0) {
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i].getModel();
if (TreeGraph.indexOfChild(data.children || [], child.id) === -1) {
self.innerRemoveChild(
child.id,
{
x: data.x!,
y: data.y!,
},
animate,
);
// 更新父节点下缓存的子节点 item 实例列表
children.splice(i, 1);
}
}
}
}
let oriX: number;
let oriY: number;
if (current.get('originAttrs')) {
oriX = current.get('originAttrs').x;
oriY = current.get('originAttrs').y;
}
const model = current.getModel();
if (animate) {
// 如果有动画,先缓存节点运动再更新节点
current.set('originAttrs', {
x: model.x,
y: model.y,
});
}
current.set('model', data.data);
if (oriX !== data.x || oriY !== data.y) {
current.updatePosition({ x: data.x, y: data.y });
}
}
/**
* Item对象
* @param id
* @param to
* @param animate
*/
private innerRemoveChild(id: string, to: Point, animate: boolean) {
const self = this;
const node: Item = self.findById(id);
if (!node) {
return;
}
each(node.get('children'), (child) => {
self.innerRemoveChild(child.getModel().id, to, animate);
});
if (animate) {
const model = node.getModel();
node.set('to', to);
node.set('originAttrs', { x: model.x, y: model.y });
self.get('removeList').push(node);
} else {
self.removeItem(node);
}
}
/**
*
* @param {object} data
*/
public changeData(data?: GraphData | TreeGraphData): any {
const self = this;
// 更改数据源后,取消所有状态
this.getNodes().map((node) => self.clearItemStates(node));
this.getEdges().map((edge) => self.clearItemStates(edge));
if (data) {
self.data(data);
self.render();
} else {
self.layout(this.get('fitView'));
}
}
/**
* updateLayout
*
* @param {object} layout
*/
public changeLayout(layout: any) {
// eslint-disable-next-line no-console
console.warn(
'Please call updateLayout instead of changeLayout. changeLayout will be discarded soon',
);
const self = this;
self.updateLayout(layout);
}
/**
*
* @param {object} layout
*/
public updateLayout(layout: any) {
const self = this;
if (!layout) {
// eslint-disable-next-line no-console
console.warn('layout cannot be null');
return;
}
self.set('layout', layout);
self.set('layoutMethod', self.getLayout());
self.layout();
}
/**
* layout
* data
* @param {boolean} fitView
*/
public refreshLayout(fitView?: boolean) {
// eslint-disable-next-line no-console
console.warn(
'Please call layout instead of refreshLayout. refreshLayout will be discarded soon',
);
const self = this;
self.layout(fitView);
}
/**
* data
* @param {boolean} fitView
*/
public layout(fitView?: boolean) {
const self = this;
const data: TreeGraphData = self.get('data');
const layoutMethod = self.get('layoutMethod');
const layoutData = layoutMethod(data, self.get('layout'));
const animate: boolean = self.get('animate');
self.emit('beforerefreshlayout', { data, layoutData });
self.emit('beforelayout');
self.innerUpdateChild(layoutData, undefined, animate);
if (fitView) {
const viewController = self.get('viewController');
viewController.fitView();
}
if (!animate) {
// 如果没有动画,目前仅更新了节点的位置,刷新一下边的样式
self.refresh();
self.paint();
} else {
self.layoutAnimate(layoutData);
}
self.emit('afterrefreshlayout', { data, layoutData });
self.emit('afterlayout');
}
/**
* id
* @param {TreeGraphData} data
* @param {string} parent id
*/
public addChild(data: TreeGraphData, parent: string | Item): void {
const self = this;
self.emit('beforeaddchild', { model: data, parent });
// 将数据添加到源数据中走changeData方法
if (!isString(parent)) {
parent = parent.get('id') as string;
}
const parentData = self.findDataById(parent);
if (parentData) {
if (!parentData.children) {
parentData.children = [];
}
parentData.children.push(data);
self.changeData();
}
}
/**
*
* @param {TreeGraphData[]} data
* @param {string} parent id
*/
public updateChildren(data: TreeGraphData[], parentId: string): void {
const self = this;
// 如果没有父节点或找不到该节点是全量的更新直接重置data
if (!parentId || !self.findById(parentId)) {
console.warn(`Update children failed! There is no node with id '${parentId}'`);
return;
}
const parentModel = self.findDataById(parentId) as NodeConfig;
parentModel.children = data;
self.changeData();
}
/**
*
* @param {TreeGraphData} data
* @param {string} parentId id
*/
public updateChild(data: TreeGraphData, parentId?: string): void {
const self = this;
// 如果没有父节点或找不到该节点是全量的更新直接重置data
if (!parentId || !self.findById(parentId)) {
self.changeData(data);
return;
}
const parentModel = self.findDataById(parentId) as NodeConfig;
const current = self.findById(data.id);
if (!parentModel.children) {
// 当 current 不存在时children 为空数组
parentModel.children = [];
}
// 如果不存在该节点,则添加
if (!current) {
parentModel.children.push(data);
} else {
const index = TreeGraph.indexOfChild(parentModel.children, data.id);
parentModel.children[index] = data;
}
self.changeData();
}
/**
*
* @param {string} id id
*/
public removeChild(id: string): void {
const self = this;
const node = self.findById(id);
if (!node) {
return;
}
const parent = node.get('parent');
if (parent && !parent.destroyed) {
const parentNode = self.findDataById(parent.get('id'));
const siblings = (parentNode && parentNode.children) || [];
const model: NodeConfig = node.getModel() as NodeConfig;
const index = TreeGraph.indexOfChild(siblings, model.id);
siblings.splice(index, 1);
}
self.changeData();
}
/**
* id获取对应的源数据
* @param {string} id id
* @param {TreeGraphData | undefined} parent
* @return {TreeGraphData}
*/
public findDataById(id: string, parent?: TreeGraphData | undefined): TreeGraphData | null {
const self = this;
if (!parent) {
parent = self.get('data') as TreeGraphData;
}
if (id === parent.id) {
return parent;
}
let result: TreeGraphData | null = null;
// eslint-disable-next-line consistent-return
each(parent.children || [], (child) => {
if (child.id === id) {
result = child;
return false;
}
result = self.findDataById(id, child);
if (result) {
return false;
}
});
return result;
}
/**
*
* @param {TreeGraphData} data
* @param {function} onFrame
*/
public layoutAnimate(
data: TreeGraphData,
onFrame?: (
item: Item,
ratio: number,
originAttrs?: ShapeStyle,
data?: TreeGraphData,
) => unknown,
): void {
const self = this;
const animateCfg = this.get('animateCfg');
self.emit('beforeanimate', { data });
// 如果边中没有指定锚点,但是本身有锚点控制,在动画过程中保持锚点不变
self.getEdges().forEach((edge) => {
const model = edge.get('model');
if (!model.sourceAnchor) {
model.sourceAnchor = edge.get('sourceAnchorIndex');
}
});
this.get('canvas').animate(
(ratio: number) => {
traverseTree<TreeGraphData>(data, (child) => {
const node = self.findById(child.id);
// 只有当存在node的时候才执行
if (node) {
let origin = node.get('originAttrs');
const model = node.get('model');
if (!origin) {
origin = {
x: model.x,
y: model.y,
};
node.set('originAttrs', origin);
}
if (onFrame) {
const attrs = onFrame(node, ratio, origin, data);
node.set('model', Object.assign(model, attrs));
} else {
model.x = origin.x + (child.x! - origin.x) * ratio;
model.y = origin.y + (child.y! - origin.y) * ratio;
}
}
return true;
});
each(self.get('removeList'), (node) => {
const model = node.getModel();
const from = node.get('originAttrs');
const to = node.get('to');
model.x = from.x + (to.x - from.x) * ratio;
model.y = from.y + (to.y - from.y) * ratio;
});
self.refreshPositions();
},
{
duration: animateCfg.duration,
easing: animateCfg.ease,
callback: () => {
each(self.getNodes(), (node) => {
node.set('originAttrs', null);
});
each(self.get('removeList'), (node) => {
self.removeItem(node);
});
self.set('removeList', []);
if (animateCfg.callback) {
animateCfg.callback();
}
self.emit('afteranimate', { data });
},
delay: animateCfg.delay,
},
);
}
/**
*
*/
public stopLayoutAnimate(): void {
this.get('canvas').stopAnimate();
this.emit('layoutanimateend', { data: this.get('data') });
this.layoutAnimating = false;
}
/**
*
* @return {boolean}
*/
public isLayoutAnimating(): boolean {
return this.layoutAnimating;
}
/**
* data接口的数据渲染视图
*/
public render(): void {
const self = this;
const data: TreeGraphData = self.get('data');
if (!data) {
throw new Error('data must be defined first');
}
self.clear();
self.emit('beforerender');
self.layout(this.get('fitView'));
self.emit('afterrender');
}
/**
*
* @return {object} data
*/
public save(): TreeGraphData | GraphData {
return this.get('data');
}
}
G6.TreeGraph = TreeGraph;
}
export default getExtender(treeGraphExtender);

View File

@ -11,7 +11,6 @@ import { ICanvas, IGroup, IShape } from '@antv/g-base';
import * as Algorithm from '@antv/algorithm';
import Graph, { registerGraph as oRegisterGraph } from './graph/graph';
import { Layout, registerLayout } from './layout';
import { IExtender } from './interface/extend';
import Global from './global';
import Util from './util';
import './element';

View File

@ -1,5 +0,0 @@
export interface IExtender {
(option: any, G6: any): void;
installed: boolean;
};

View File

@ -1,7 +0,0 @@
import { IExtender } from '../interface/extend';
export function getExtender(extenderFn: any): IExtender {
let extender = extenderFn;
extender.installed = false;
return extender;
}