fix: 新增可以修改整体样式的接口

This commit is contained in:
黎智洲 2021-12-15 19:51:41 +08:00
parent e1a69b5e91
commit 3626a6eb19
18 changed files with 166 additions and 188 deletions

View File

@ -6,7 +6,7 @@ SV.registerLayout('Array', {
sourcesPreprocess(sources) {
const firstElement = sources[0];
if(firstElement.external) {
if (firstElement.external) {
firstElement.headExternal = firstElement.external;
delete firstElement.external;
}
@ -16,9 +16,9 @@ SV.registerLayout('Array', {
defineOptions() {
return {
node: {
node: {
default: {
type: 'rect',
type: 'array-node',
label: '[id]',
size: [60, 30],
labelOptions: {
@ -56,77 +56,13 @@ SV.registerLayout('Array', {
layout(elements) {
let arr = elements;
for(let i = 0; i < arr.length; i++) {
for (let i = 0; i < arr.length; i++) {
let width = arr[i].get('size')[0];
if(i > 0) {
if (i > 0) {
arr[i].set('x', arr[i - 1].get('x') + width);
}
}
}
});
});
SV.registerLayout('Array', {
sourcesPreprocess(sources) {
const firstElement = sources[0];
if(firstElement.external) {
firstElement.headExternal = firstElement.external;
delete firstElement.external;
}
return sources;
},
defineOptions() {
return {
node: {
default: {
type: 'indexed-node',
label: '[id]',
size: [60, 30],
style: {
stroke: '#333',
fill: '#355c7d'
}
}
},
marker: {
headExternal: {
type: 'pointer',
anchor: 3,
style: {
fill: '#f08a5d'
}
},
external: {
type: 'pointer',
anchor: 0,
style: {
fill: '#f08a5d'
}
}
},
indexLabel: {
index: { position: 'bottom' },
indexTop: { position: 'top' }
}
};
},
layout(elements) {
let arr = elements;
for(let i = 0; i < arr.length; i++) {
let width = arr[i].get('size')[0];
if(i > 0) {
arr[i].set('x', arr[i - 1].get('x') + width);
}
}
}
}, 'colorful');

View File

@ -100,27 +100,8 @@
let data = [{
PTree: {
data: [
{
id: '1001',
data: 'A',
parent: -1,
index: 0
},
{
id: '1002',
data: 'B',
parent: 0,
index: 1
},
],
layouter: 'PTree'
}
}, {
Array: {
data: [{ id: 1, data: 1 }, { id: 2, data: 2 }, { id: 3, data: 3 }],
data: [{ id: 1, data: 1, external: 'list' }, { id: 2, data: 2 }, { id: 3, data: 3 }],
layouter: 'Array'
}
}, {
@ -172,7 +153,7 @@
});
document.getElementById('switch-mode').addEventListener('click', e => {
cur.switchMode('Array', 'colorful');
cur.updateStyle('Array', newArrayOption);
});
const leak = document.getElementById('leak');

40
dist/sv.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,10 @@
{
"dependencies": {
"@antv/g6": "^4.4.1"
"@antv/g6": "^4.4.1",
"merge": "^2.1.1"
},
"devDependencies": {
"merge": "^2.1.1",
"ts-loader": "^5.2.1",
"typescript": "^3.2.2",
"webpack": "^4.46.0",

View File

@ -24,8 +24,7 @@ export class SVLink extends SVModel {
this.G6ModelProps = this.generateG6ModelProps(options);
}
protected generateG6ModelProps(options: LinkOption): EdgeConfig {
generateG6ModelProps(options: LinkOption): EdgeConfig {
let sourceAnchor = options.sourceAnchor,
targetAnchor = options.targetAnchor;

View File

@ -3,7 +3,7 @@ import { Style } from "../options";
import { BoundingRect } from "../Common/boundingRect";
import { EdgeConfig, Item, NodeConfig } from "@antv/g6-core";
import { Graph } from "_@antv_g6-pc@0.5.0@@antv/g6-pc";
import merge from 'merge';
@ -52,7 +52,7 @@ export class SVModel {
* G6 model
* @param option
*/
protected generateG6ModelProps(options: unknown) {
generateG6ModelProps(options: unknown): NodeConfig | EdgeConfig {
return null;
}
@ -87,7 +87,7 @@ export class SVModel {
}
if (attr === 'style' || attr === 'labelCfg') {
Object.assign(this.G6ModelProps[attr], value);
this.G6ModelProps[attr] = merge(this.G6ModelProps[attr] || {}, value);
}
else {
this.G6ModelProps[attr] = value;
@ -115,6 +115,31 @@ export class SVModel {
}
}
/**
*
* @param G6ModelProps
*/
updateG6ModelStyle(G6ModelProps: NodeConfig | EdgeConfig) {
const newG6ModelProps = {
style: {
...G6ModelProps.style
},
labelCfg: {
...G6ModelProps.labelCfg
}
};
this.G6ModelProps = merge(this.G6ModelProps, newG6ModelProps);
if (this.G6Item) {
this.g6Instance.updateItem(this.G6Item, this.G6ModelProps);
}
if (this.shadowG6Item) {
this.shadowG6Instance.updateItem(this.shadowG6Item, this.G6ModelProps);
}
}
/**
*
* @returns

View File

@ -51,7 +51,7 @@ export class SVNode extends SVModel {
this.G6ModelProps = this.generateG6ModelProps(options);
}
protected generateG6ModelProps(options: NodeOption): NodeConfig {
generateG6ModelProps(options: NodeOption): NodeConfig {
const style = Util.objectClone<Style>(options.style);
return {

View File

@ -1,7 +1,6 @@
import { INode, NodeConfig } from "@antv/g6-core";
import { INode, NodeConfig, EdgeConfig } from "@antv/g6-core";
import { Util } from "../Common/util";
import { AddressLabelOption, IndexLabelOption, MarkerOption, NodeLabelOption, Style } from "../options";
import { SourceNode } from "../sources";
import { SVModel } from "./SVModel";
import { SVNode } from "./SVNode";
@ -108,7 +107,7 @@ export class SVIndexLabel extends SVNodeAppendage {
this.G6ModelProps = this.generateG6ModelProps(options) as NodeConfig;
}
generateG6ModelProps(options: IndexLabelOption) {
generateG6ModelProps(options: IndexLabelOption): NodeConfig | EdgeConfig {
return {
id: this.id,
x: 0,
@ -155,7 +154,7 @@ export class SVMarker extends SVNodeAppendage {
this.G6ModelProps = this.generateG6ModelProps(options);
}
protected generateG6ModelProps(options: MarkerOption): NodeConfig {
generateG6ModelProps(options: MarkerOption): NodeConfig {
this.anchor = options.anchor;
const type = options.type,

View File

@ -44,13 +44,12 @@ export class ModelConstructor {
*/
public construct(sources: Sources): LayoutGroupTable {
const layoutGroupTable = new Map<string, LayoutGroup>(),
layoutMap: { [key: string]: { [key: string]: LayoutCreator } } = SV.registeredLayout;
layoutMap: { [key: string]: LayoutCreator } = SV.registeredLayout;
Object.keys(sources).forEach(group => {
let sourceGroup = sources[group],
layout = sourceGroup.layouter,
mode = sourceGroup.mode || 'default',
layoutCreator: LayoutCreator = layoutMap[layout][mode];
layoutCreator: LayoutCreator = layoutMap[layout];
if (!layout || !layoutCreator) {
return;
@ -120,13 +119,6 @@ export class ModelConstructor {
return this.layoutGroupTable;
}
/**
*
* @returns
*/
public getLayoutGroupTable(): LayoutGroupTable {
return this.layoutGroupTable;
}
/**
* node
@ -430,6 +422,14 @@ export class ModelConstructor {
return counter <= 2;
}
/**
*
* @returns
*/
public getLayoutGroupTable(): LayoutGroupTable {
return this.layoutGroupTable;
}
/**
*
*/

View File

@ -0,0 +1,13 @@
import G6 from '@antv/g6';
export default G6.registerNode('array-node', {
getAnchorPoints() {
return [
[0.5, 0],
[1, 0.5],
[0.5, 1],
[0, 0.5]
];
}
}, 'rect');

View File

@ -16,7 +16,7 @@ export default registerNode('binary-tree-node', {
height: height,
stroke: cfg.style.stroke || '#333',
cursor: cfg.style.cursor,
fill: '#eee'
fill: cfg.style.backgroundFill || '#eee'
},
name: 'wrapper'
});

View File

@ -15,7 +15,7 @@ export default registerNode('link-list-node', {
width: width,
height: height,
stroke: cfg.style.stroke || '#333',
fill: '#eee',
fill: cfg.style.backgroundFill || '#eee',
cursor: cfg.style.cursor
},
name: 'wrapper'

View File

@ -16,7 +16,7 @@ export default registerNode('tri-tree-node', {
height: height,
stroke: cfg.style.stroke || '#333',
cursor: cfg.style.cursor,
fill: '#eee'
fill: cfg.style.backgroundFill || '#eee'
},
name: 'wrapper'
});

View File

@ -16,7 +16,7 @@ export default registerNode('two-cell-node', {
width: width,
height: height,
stroke: cfg.style.stroke,
fill: '#eee'
fill: cfg.style.backgroundFill || '#eee'
},
name: 'wrapper'
});

View File

@ -1,12 +1,13 @@
import { Engine } from "./engine";
import { Bound } from "./Common/boundingRect";
import { Group } from "./Common/group";
import pointer from "./RegisteredShape/pointer";
import G6, { Util } from '@antv/g6';
import linkListNode from "./RegisteredShape/linkListNode";
import binaryTreeNode from "./RegisteredShape/binaryTreeNode";
import Pointer from "./RegisteredShape/pointer";
import LinkListNode from "./RegisteredShape/linkListNode";
import BinaryTreeNode from "./RegisteredShape/binaryTreeNode";
import CLenQueuePointer from "./RegisteredShape/clenQueuePointer";
import twoCellNode from "./RegisteredShape/twoCellNode";
import TwoCellNode from "./RegisteredShape/twoCellNode";
import ArrayNode from "./RegisteredShape/arrayNode";
import Cursor from "./RegisteredShape/cursor";
import { Vector } from "./Common/vector";
import { EngineOptions, LayoutCreator } from "./options";
@ -14,7 +15,6 @@ import { SVNode } from "./Model/SVNode";
import { SourceNode } from "./sources";
export interface StructV {
(DOMContainer: HTMLElement, engineOptions: EngineOptions): Engine;
Group: typeof Group;
@ -25,7 +25,7 @@ export interface StructV {
registeredShape: any[];
registeredLayout: { [key: string]: { [key: string]: LayoutCreator } },
registeredLayout: { [key: string]: LayoutCreator },
registerShape: Function,
@ -50,16 +50,17 @@ SV.G6 = G6;
SV.registeredLayout = {};
SV.registeredShape = [
pointer,
linkListNode,
binaryTreeNode,
twoCellNode,
Pointer,
LinkListNode,
BinaryTreeNode,
TwoCellNode,
Cursor,
ArrayNode,
CLenQueuePointer,
];
SV.registerShape = G6.registerNode;
SV.registerLayout = function(name: string, layoutCreator: LayoutCreator, mode: string = 'default') {
SV.registerLayout = function(name: string, layoutCreator: LayoutCreator) {
if(typeof layoutCreator.sourcesPreprocess !== 'function') {
layoutCreator.sourcesPreprocess = function(data: SourceNode[]): SourceNode[] {
@ -76,12 +77,8 @@ SV.registerLayout = function(name: string, layoutCreator: LayoutCreator, mode: s
if(typeof layoutCreator.defineOptions !== 'function' || typeof layoutCreator.layout !== 'function') {
return;
}
if(SV.registeredLayout[name] === undefined) {
SV.registeredLayout[name] = {};
}
SV.registeredLayout[name][mode] = layoutCreator;
SV.registeredLayout[name] = layoutCreator;
};

View File

@ -25,13 +25,11 @@ export class Reconcile {
private engine: Engine;
private renderer: Renderer;
private prevChangeModels: SVModel[];
private isFirstPatch: boolean;
constructor(engine: Engine, renderer: Renderer) {
this.engine = engine;
this.renderer = renderer;
this.prevChangeModels = [];
this.isFirstPatch = true;
}
@ -318,10 +316,6 @@ export class Reconcile {
* @param models
*/
private handleChangeModels(models: SVModel[]) {
if (models.length === 0) {
models = this.prevChangeModels;
}
const changeHighlightColor: string = this.engine.viewOptions.updateHighlight;
if (!changeHighlightColor || typeof changeHighlightColor !== 'string') {
@ -344,8 +338,6 @@ export class Reconcile {
});
}
});
this.prevChangeModels = models;
}
@ -415,7 +407,5 @@ export class Reconcile {
}
}
public destroy() {
this.prevChangeModels.length = 0;
}
public destroy() { }
}

View File

@ -1,6 +1,6 @@
import { Sources } from "./sources";
import { ModelConstructor } from "./Model/modelConstructor";
import { AnimationOptions, EngineOptions, InteractionOptions, ViewOptions } from "./options";
import { AnimationOptions, EngineOptions, InteractionOptions, LayoutGroupOptions, LayoutOptions, ViewOptions } from "./options";
import { EventBus } from "./Common/eventBus";
import { ViewContainer } from "./View/viewContainer";
import { SVNode } from "./Model/SVNode";
@ -8,12 +8,12 @@ import { Util } from "./Common/util";
import { SVModel } from "./Model/SVModel";
export class Engine {
export class Engine {
private modelConstructor: ModelConstructor;
private viewContainer: ViewContainer;
private prevSource: Sources;
private prevStringSource: string;
public engineOptions: EngineOptions;
public viewOptions: ViewOptions;
public animationOptions: AnimationOptions;
@ -50,14 +50,15 @@ export class Engine {
/**
*
* @param sources
* @param force
*/
public render(source: Sources) {
if(source === undefined || source === null) {
public render(source: Sources, force: boolean = false) {
if (source === undefined || source === null) {
return;
}
``
``
let stringSource = JSON.stringify(source);
if(this.prevStringSource === stringSource) {
if (force === false && this.prevStringSource === stringSource) {
return;
}
@ -66,31 +67,11 @@ export class Engine {
// 1 转换模型data => model
const layoutGroupTable = this.modelConstructor.construct(source);
// 2 渲染使用g6进行渲染
this.viewContainer.render(layoutGroupTable);
}
/**
* mode
* @param mode
*/
public switchMode(layout: string, mode: string) {
if(this.prevSource === undefined || this.prevSource === null) {
return;
}
Object.keys(this.prevSource).map(group => {
let sourceGroup = this.prevSource[group];
if(sourceGroup.layouter === layout) {
sourceGroup.mode = mode;
}
});
this.render(this.prevSource);
}
/**
*
@ -111,19 +92,19 @@ export class Engine {
* @param groupNames
*/
public hideGroups(groupNames: string | string[]) {
const names = Array.isArray(groupNames)? groupNames: [groupNames],
instance = this.viewContainer.getG6Instance(),
layoutGroupTable = this.modelConstructor.getLayoutGroupTable();
const names = Array.isArray(groupNames) ? groupNames : [groupNames],
instance = this.viewContainer.getG6Instance(),
layoutGroupTable = this.modelConstructor.getLayoutGroupTable();
layoutGroupTable.forEach(item => {
const hasName = names.find(name => name === item.layout);
if(hasName && !item.isHide) {
if (hasName && !item.isHide) {
item.modelList.forEach(model => instance.hideItem(model.G6Item));
item.isHide = true;
}
if(!hasName && item.isHide) {
if (!hasName && item.isHide) {
item.modelList.forEach(model => instance.showItem(model.G6Item));
item.isHide = false;
}
@ -140,6 +121,38 @@ export class Engine {
return [...modelList, ...accumulateLeakModels];
}
/**
*
* @param modelType
* @returns
*/
public updateStyle(group: string, newOptions: LayoutGroupOptions) {
const models = this.getAllModels(),
layoutGroup = this.modelConstructor.getLayoutGroupTable().get(group);
layoutGroup.options = newOptions;
models.forEach(item => {
if (item.group !== group) {
return;
}
const modelType = item.getModelType(),
optionsType = layoutGroup.options[modelType];
if (optionsType) {
if (modelType === 'addressLabel') {
item.updateG6ModelStyle(item.generateG6ModelProps(optionsType));
}
else {
const targetModelOption = optionsType[item.sourceType];
if (targetModelOption) {
item.updateG6ModelStyle(item.generateG6ModelProps(targetModelOption));
}
}
}
});
}
/**
* 使id查找某个节点
* @param id
@ -167,16 +180,16 @@ export class Engine {
* @param callback
*/
public on(eventName: string, callback: Function) {
if(typeof callback !== 'function') {
if (typeof callback !== 'function') {
return;
}
if(eventName === 'onFreed' || eventName === 'onLeak') {
if (eventName === 'onFreed' || eventName === 'onLeak') {
EventBus.on(eventName, callback);
return;
}
if(eventName === 'onLeakAreaUpdate') {
if (eventName === 'onLeakAreaUpdate') {
EventBus.on(eventName, callback);
return;
}

View File

@ -19,7 +19,6 @@ export type Sources = {
[key: string]: {
data: SourceNode[];
layouter: string;
mode?: string
}
};