改进可视化细节: pointer改为marker

This commit is contained in:
黎智洲 2021-07-29 23:44:49 +08:00
parent da2d43581e
commit 461a6242da
8 changed files with 96 additions and 56 deletions

2
dist/sv.js vendored

File diff suppressed because one or more lines are too long

View File

@ -153,24 +153,26 @@ export class ModelConstructor {
if(Array.isArray(sourceLinkData)) { if(Array.isArray(sourceLinkData)) {
element[name] = sourceLinkData.map((item, index) => { element[name] = sourceLinkData.map((item, index) => {
targetElement = this.fetchTargetElements(layoutGroupTable, element, item); targetElement = this.fetchTargetElements(layoutGroupTable, element, item);
let isGeneralLink = this.isGeneralLink(sourceLinkData.toString());
if(targetElement) { if(targetElement) {
link = this.createLink(name, element, targetElement, index, linkOptions[name]); link = this.createLink(name, element, targetElement, index, linkOptions[name]);
linkList.push(link); linkList.push(link);
} }
return targetElement; return isGeneralLink? targetElement: null;
}); });
} }
else { else {
targetElement = this.fetchTargetElements(layoutGroupTable, element, sourceLinkData); targetElement = this.fetchTargetElements(layoutGroupTable, element, sourceLinkData);
let isGeneralLink = this.isGeneralLink(sourceLinkData.toString());
if(targetElement) { if(targetElement) {
link = this.createLink(name, element, targetElement, null, linkOptions[name]); link = this.createLink(name, element, targetElement, null, linkOptions[name]);
linkList.push(link); linkList.push(link);
} }
element[name] = targetElement; element[name] = isGeneralLink? targetElement: null;
} }
} }
}); });
@ -356,6 +358,22 @@ export class ModelConstructor {
return targetElement || null; return targetElement || null;
} }
/**
* group
* @param linkId
*/
private isGeneralLink(linkId: string): boolean {
let counter = 0;
for(let i = 0; i < linkId.length; i++) {
if(linkId[i] === '#') {
counter++;
}
}
return counter <= 2;
}
/** /**
* *
*/ */

View File

@ -170,6 +170,10 @@ export class Element extends Model {
layouterName: string; layouterName: string;
freed: boolean; freed: boolean;
markers: { [key: string]: Marker }; markers: { [key: string]: Marker };
links: {
inDegree: Link[];
outDegree: Link[];
};
constructor(id: string, type: string, group: string, layouter: string, sourceElement: SourceElement) { constructor(id: string, type: string, group: string, layouter: string, sourceElement: SourceElement) {
super(id, type); super(id, type);
@ -191,6 +195,7 @@ export class Element extends Model {
this.sourceId = this.id.split('.')[1]; this.sourceId = this.id.split('.')[1];
this.sourceElement = sourceElement; this.sourceElement = sourceElement;
this.markers = { }; this.markers = { };
this.links = { inDegree: [], outDegree: [] };
} }
protected defineProps(option: ElementOption): G6NodeModel { protected defineProps(option: ElementOption): G6NodeModel {
@ -226,6 +231,9 @@ export class Link extends Model {
this.element = element; this.element = element;
this.target = target; this.target = target;
this.index = index; this.index = index;
element.links.outDegree.push(this);
target.links.inDegree.push(this);
} }

View File

@ -19,7 +19,7 @@ export class Container {
protected afterAppendModelsCallbacks: ((models: Model[]) => void)[] = []; protected afterAppendModelsCallbacks: ((models: Model[]) => void)[] = [];
protected afterRemoveModelsCallbacks: ((models: Model[]) => void)[] = []; protected afterRemoveModelsCallbacks: ((models: Model[]) => void)[] = [];
constructor(engine: Engine, DOMContainer: HTMLElement, g6Options: { [key: string]: any } = { }) { constructor(engine: Engine, DOMContainer: HTMLElement, g6Options: { [key: string]: any } = {}) {
this.engine = engine; this.engine = engine;
this.DOMContainer = DOMContainer; this.DOMContainer = DOMContainer;
this.animationsOptions = engine.animationOptions; this.animationsOptions = engine.animationOptions;
@ -28,24 +28,14 @@ export class Container {
const g6Plugins = []; const g6Plugins = [];
if(g6Options.tooltip) { if (g6Options.tooltip) {
const tooltip = new SV.G6.Tooltip({ const tooltip = new SV.G6.Tooltip({
offsetX: 10, offsetX: 10,
offsetY: 20, offsetY: 20,
shouldBegin(event) { shouldBegin(event) {
return event.item.getModel().SVModelType === 'element'; return event.item.getModel().SVModelType === 'element';
}, },
getContent(event) { getContent: event => this.getTooltipContent(event.item.SVModel, { address: 'sourceId', data: 'data' }),
const data = event.item.SVModel.data,
wrapper = document.createElement('div');
wrapper.style.padding = '0 4px 0 4px';
wrapper.innerHTML = `
<h5>id: ${ event.item.SVModel.sourceId }</h5>
<h5>data: ${ data? data: '' }</h5>
`
return wrapper;
},
itemTypes: ['node'] itemTypes: ['node']
}); });
@ -63,6 +53,23 @@ export class Container {
this.afterInitRenderer(); this.afterInitRenderer();
} }
private getTooltipContent(model: Model, items: { [key: string]: string }): HTMLElement {
const wrapper = document.createElement('div');
Object.keys(items).map(key => {
let value = model[items[key]];
if (value !== undefined && value !== null) {
let item = document.createElement('div');
item.innerHTML = `${key}${value}`;
wrapper.appendChild(item);
}
});
return wrapper;
}
/** /**
* *
* @param optionsTable * @param optionsTable
@ -114,14 +121,14 @@ export class Container {
list.forEach(item => { list.forEach(item => {
const prevItem = this.prevModelList.find(prevItem => prevItem.id === item.id); const prevItem = this.prevModelList.find(prevItem => prevItem.id === item.id);
if(prevItem === undefined) { if (prevItem === undefined) {
return; return;
} }
const prevLabel = prevItem.get('label'), const prevLabel = prevItem.get('label'),
label = item.get('label'); label = item.get('label');
if(prevLabel !== label) { if (prevLabel !== label) {
labelChangeModels.push(item); labelChangeModels.push(item);
} }
}); });
@ -143,7 +150,7 @@ export class Container {
callback: () => { callback: () => {
counter++; counter++;
if(counter === appendModels.length) { if (counter === appendModels.length) {
this.afterAppendModelsCallbacks.map(item => item(appendModels)); this.afterAppendModelsCallbacks.map(item => item(appendModels));
} }
} }
@ -163,14 +170,14 @@ export class Container {
duration: this.animationsOptions.duration, duration: this.animationsOptions.duration,
timingFunction: this.animationsOptions.timingFunction, timingFunction: this.animationsOptions.timingFunction,
callback: () => { callback: () => {
if(item.isLeak === false) { if (item.isLeak === false) {
this.renderer.removeModel(item); this.renderer.removeModel(item);
item.renderG6Item = item.G6Item = null; item.renderG6Item = item.G6Item = null;
} }
counter++; counter++;
if(counter === removeModels.length) { if (counter === removeModels.length) {
this.afterRemoveModelsCallbacks.map(item => item(removeModels)); this.afterRemoveModelsCallbacks.map(item => item(removeModels));
} }
} }
@ -223,7 +230,7 @@ export class Container {
this.handleRemoveModels(removeModels); this.handleRemoveModels(removeModels);
this.handleChangeModels(changeModels); this.handleChangeModels(changeModels);
if(this.renderer.getIsFirstRender()) { if (this.renderer.getIsFirstRender()) {
this.renderer.setIsFirstRender(false); this.renderer.setIsFirstRender(false);
} }

View File

@ -45,7 +45,7 @@ import { Container } from "./container";
return false; return false;
} }
if(model.SVModelType === 'pointer') { if(model.SVModelType === 'marker') {
return false; return false;
} }
@ -69,7 +69,7 @@ import { Container } from "./container";
return false; return false;
} }
if(model.SVModelType === 'pointer') { if(model.SVModelType === 'marker') {
return false; return false;
} }
@ -105,16 +105,16 @@ import { Container } from "./container";
*/ */
protected afterInitRenderer() { protected afterInitRenderer() {
let g6Instance = this.getG6Instance(), let g6Instance = this.getG6Instance(),
pointer = null, marker = null,
pointerX = null, markerX = null,
pointerY = null, markerY = null,
dragStartX = null, dragStartX = null,
dragStartY = null; dragStartY = null;
g6Instance.on('node:dragstart', ev => { g6Instance.on('node:dragstart', ev => {
const model = ev.item.getModel(); const model = ev.item.getModel();
if(model.SVModelType === 'pointer') { if(model.SVModelType === 'marker') {
return; return;
} }
@ -128,26 +128,26 @@ import { Container } from "./container";
return; return;
} }
pointer = g6Instance.findById(model.externalPointerId); marker = g6Instance.findById(model.markerId);
if(pointer) { if(marker) {
pointerX = pointer.getModel().x, markerX = marker.getModel().x,
pointerY = pointer.getModel().y; markerY = marker.getModel().y;
dragStartX = ev.canvasX; dragStartX = ev.canvasX;
dragStartY = ev.canvasY; dragStartY = ev.canvasY;
} }
}); });
g6Instance.on('node:dragend', ev => { g6Instance.on('node:dragend', ev => {
pointer = null; marker = null;
pointerX = null, markerX = null,
pointerY = null, markerY = null,
dragStartX = null, dragStartX = null,
dragStartY = null; dragStartY = null;
}); });
g6Instance.on('node:drag', ev => { g6Instance.on('node:drag', ev => {
if(!pointer) { if(!marker) {
return; return;
} }
@ -155,9 +155,9 @@ import { Container } from "./container";
dy = ev.canvasY - dragStartY, dy = ev.canvasY - dragStartY,
zoom = g6Instance.getZoom(); zoom = g6Instance.getZoom();
pointer.updatePosition({ marker.updatePosition({
x: pointerX + dx / zoom, x: markerX + dx / zoom,
y: pointerY + dy / zoom y: markerY + dy / zoom
}); });
}); });
} }

View File

@ -92,8 +92,11 @@ export class Renderer {
modelList.forEach(item => { modelList.forEach(item => {
item.renderG6Item = this.g6Instance.findById(item.id); item.renderG6Item = this.g6Instance.findById(item.id);
if(item.renderG6Item) {
item.G6Item = item.renderG6Item; item.G6Item = item.renderG6Item;
item.renderG6Item.SVModel = item; item.renderG6Item.SVModel = item;
}
}); });
// 把所有连线置顶 // 把所有连线置顶

View File

@ -68,7 +68,7 @@ export class ViewManager {
removeModels: Model[] = []; removeModels: Model[] = [];
layoutGroupTable.forEach((group, key) => { layoutGroupTable.forEach((group, key) => {
let freedElements: Model[] = group.element.filter(item => item.freed); let freedElements: Element[] = group.element.filter(item => item.freed);
if(freedElements.length) { if(freedElements.length) {
freedGroupName = key; freedGroupName = key;
@ -107,6 +107,7 @@ export class ViewManager {
elements.forEach(item => { elements.forEach(item => {
elementIds.push(item.id); elementIds.push(item.id);
item.set('style', { item.set('style', {
fill: '#ccc' fill: '#ccc'
}); });
@ -189,7 +190,7 @@ export class ViewManager {
renderAll(layoutGroupTable: LayoutGroupTable) { renderAll(layoutGroupTable: LayoutGroupTable) {
this.shadowG6Instance.clear(); this.shadowG6Instance.clear();
const modelList = Util.convertGroupTable2ModelList(layoutGroupTable); let modelList = Util.convertGroupTable2ModelList(layoutGroupTable);
this.build(modelList); this.build(modelList);
@ -209,6 +210,8 @@ export class ViewManager {
// 进行布局设置model的xy // 进行布局设置model的xy
this.layouter.layoutAll(this.mainContainer, layoutGroupTable); this.layouter.layoutAll(this.mainContainer, layoutGroupTable);
// 从新获取一次因为第一次获取没有把freed节点筛选出去
modelList = Util.convertGroupTable2ModelList(layoutGroupTable);
this.mainContainer.render(modelList); this.mainContainer.render(modelList);
if(this.leakContainer) { if(this.leakContainer) {

View File

@ -55,6 +55,7 @@ export interface LinkOption {
export interface MarkerOption extends ElementOption { export interface MarkerOption extends ElementOption {
type: 'pointer' | 'cursor';
anchor: number; anchor: number;
offset: number; offset: number;
labelOffset: number; labelOffset: number;