feat: fisheye with dragging. feat: fisheye with scaling range and d.

This commit is contained in:
Yanyan-Wang 2020-08-18 13:56:03 +08:00 committed by Yanyan Wang
parent 0cdc30fe6a
commit e3a9ba0ec3
4 changed files with 260 additions and 18 deletions

View File

@ -3,12 +3,17 @@ import { IG6GraphEvent, ShapeStyle } from '../../types';
import Graph from '../../graph/graph';
import Base from '../base';
const DELTA = 0.05;
interface FisheyeConfig {
trigger?: 'mousemove' | 'click';
trigger?: 'mousemove' | 'click' | 'drag';
d?: number;
r?: number;
delegateStyle?: ShapeStyle;
showLabel?: boolean;
wheelScaleRange?: boolean;
maxR?: number;
minR?: number;
}
const lensDelegateStyle = {
@ -31,14 +36,25 @@ export default class Fisheye extends Base {
// class-methods-use-this
public getEvents() {
if ((this as any).get('trigger') === 'mousemove') {
return {
mousemove: 'magnify',
};
let events;
switch ((this as any).get('trigger')) {
case 'click':
events = {
click: 'magnify',
};
break;
case 'drag':
events = {
click: 'createDelegate',
};
break;
default:
events = {
mousemove: 'magnify',
};
break;
}
return {
click: 'magnify',
};
return events;
}
public init() {
@ -49,11 +65,61 @@ export default class Fisheye extends Base {
self.set('r2', r * r);
}
protected createDelegate(e: IG6GraphEvent) {
const self = this;
let lensDelegate = self.get('delegate');
if (!lensDelegate || lensDelegate.destroyed) {
self.magnify(e);
lensDelegate = self.get('delegate');
lensDelegate.on('dragstart', evt => {
self.set('delegateCenterDiff',
{ x: lensDelegate.attr('x') - evt.x, y: lensDelegate.attr('y') - evt.y }
);
});
lensDelegate.on('drag', evt => {
self.magnify(evt);
});
if (this.get('wheelScaleRange')) {
lensDelegate.on('mousewheel', evt => {
self.scaleRange(evt);
});
}
}
}
/**
* mousemove click
* @param param
*
* @param e wheel
*/
protected magnify(e: IG6GraphEvent) {
protected scaleRange(e: IG6GraphEvent) {
if (!e && !e.originalEvent) return;
const self = this;
const graph: Graph = self.get('graph');
let ratio;
const mousePos = graph.getPointByClient(e.clientX, e.clientY);
if ((e.originalEvent as any).wheelDelta < 0) {
ratio = 1 - DELTA;
} else {
ratio = 1 / (1 - DELTA);
}
const maxR = self.get('maxR');
const minR = self.get('minR');
let r = self.get('r');
if ((r > (maxR || graph.get('height')) && ratio > 1)
|| (r < (minR || (graph.get('height') * 0.05)) && ratio < 1)) {
ratio = 1;
}
r *= ratio;
self.set('r', r);
self.set('r2', r * r);
self.magnify(e, mousePos);
}
/**
* mousemoveclickdrag
* @param e
*/
protected magnify(e: IG6GraphEvent, mousePos?) {
const self = this;
self.restoreCache();
const graph: Graph = self.get('graph');
@ -65,7 +131,15 @@ export default class Fisheye extends Base {
const d = self.get('d');
const nodes = graph.getNodes();
const nodeLength = nodes.length;
const mCenter = { x: e.x, y: e.y };
let mCenter = mousePos ? { x: mousePos.x, y: mousePos.y } : { x: e.x, y: e.y };
if (self.get('dragging') && self.get('trigger') === 'mousemove') {
mCenter = self.get('cacheCenter');
}
const delegateCenterDiff = self.get('delegateCenterDiff');
if (delegateCenterDiff) {
mCenter.x += delegateCenterDiff.x;
mCenter.y += delegateCenterDiff.y;
}
self.updateDelegate(mCenter, r);
for (let i = 0; i < nodeLength; i++) {
const model = nodes[i].getModel();
@ -127,6 +201,12 @@ export default class Fisheye extends Base {
self.set('cachedOriginPositions', {});
}
/**
*
* @param {Point} mCenter
* @param {number} r
*/
public updateParams(cfg: FisheyeConfig) {
const self = this;
const { r, d, trigger } = cfg;
@ -137,7 +217,7 @@ export default class Fisheye extends Base {
if (!isNaN(d)) {
self.set('d', d);
}
if (trigger === 'mousemove' || trigger === 'click') {
if (trigger === 'mousemove' || trigger === 'click' || trigger === 'drag') {
self.set('trigger', trigger);
}
}
@ -164,13 +244,41 @@ export default class Fisheye extends Base {
y: mCenter.y,
...attrs,
},
name: 'lens-delegate-shape',
name: 'lens-shape',
draggable: true
});
lensDelegate.set('capture', false);
// self.get('trigger') !== 'drag' && lensDelegate.set('capture', false);
if (self.get('trigger') === 'mousemove') {
lensDelegate.on('dragstart', e => {
self.set('dragging', true);
self.set('cacheCenter', { x: e.x, y: e.y });
self.set('dragPrePos', { x: e.x, y: e.y });
});
lensDelegate.on('drag', e => {
const dragPrePos = self.get('dragPrePos');
const delta = e.x - dragPrePos.x > 0 ? 0.1 : -0.1;
const d = self.get('d');
const newD = d + delta;
if (newD < 10 && newD > 0) {
self.set('d', d + delta);
this.magnify(e);
}
self.set('dragPrePos', { x: e.x, y: e.y });
});
lensDelegate.on('dragend', e => {
self.set('dragging', false)
});
if (this.get('wheelScaleRange')) {
lensDelegate.on('mousewheel', evt => {
self.scaleRange(evt);
});
}
}
} else {
lensDelegate.attr({
x: mCenter.x,
y: mCenter.y,
r: r / 1.5
});
}
self.set('delegate', lensDelegate);

View File

@ -9,6 +9,8 @@ const FishEye = () => {
const fisheye = new G6.Fisheye({
r: 200,
showLabel: true,
trigger: 'drag',
wheelScaleRange: true
});
const colors = [
'#8FE9FF',
@ -27,7 +29,7 @@ const FishEye = () => {
graph = new G6.Graph({
container: container.current as string | HTMLElement,
width: 1000,
height: 700,
height: 600,
plugins: [fisheye],
layout: {
type: 'force',
@ -47,7 +49,6 @@ const FishEye = () => {
fill: colors[Math.floor(Math.random() * 9)],
lineWidth: 0,
};
console.log(node);
});
graph.data(data);
graph.render();

View File

@ -0,0 +1,131 @@
import React, { useState, useEffect } from 'react';
import G6 from '../../../src';
import { clone } from '@antv/util/lib';
import { IGraph } from '../../../src/interface/graph';
function generateData() {
let nodeNum = Math.floor(20 + Math.random() * 30);
let edgeNum = Math.floor(nodeNum + Math.floor(nodeNum + nodeNum * (nodeNum - 3) * Math.random() / 4));
let nodes = [];
for (let i = 0; i < nodeNum; i++) {
nodes.push({
id: 'node' + i.toString(),
label: i.toString(),
})
}
let edges = [];
let edgeIdx = 0;
while (edgeIdx < nodeNum) {
edges.push({
id: 'edge' + edgeIdx.toString(),
source: 'node' + edgeIdx.toString(),
target: 'node' + ((edgeIdx + 1) % nodeNum).toString(),
});
edgeIdx += 1;
}
while (edgeIdx < edgeNum) {
let s = Math.floor(Math.random() * nodeNum);
let t = Math.floor(Math.random() * nodeNum);
edges.push({
id: 'edge' + edgeIdx.toString(),
source: 'node' + s.toString(),
target: 'node' + t.toString(),
});
edgeIdx++;
}
return {
nodes: nodes,
edges: edges,
};
}
const AutoLayout = () => {
const container = React.useRef();
const [recommendedLayout, setRecommendedLayout] = useState("");
let CANVAS_WIDTH = 1320;
let CANVAS_HEIGHT = 696;
const layoutConfigs = [{
type: 'force',
}, {
type: 'grid'
}, {
type: 'circular'
}, {
type: 'dagre',
ranksep: 1,
nodesepFunc: d => 1
}];
const containers = [];
const containerDivs = []
layoutConfigs.forEach(config => {
const layoutContainer = React.useRef();
containers.push(layoutContainer);
containerDivs.push(
<>
<div className={config.type} ref={layoutContainer}></div>
<div style={{ textAlign: 'center', marginBottom: '16px' }}>{config.type}</div>
</>);
});
useEffect(() => {
if (container && container.current) {
CANVAS_WIDTH = (container.current as HTMLElement).offsetWidth; // 1320;
CANVAS_HEIGHT = (container.current as HTMLElement).offsetHeight; // 696;
}
const graph = new G6.Graph({
container: container.current as string | HTMLElement,
width: CANVAS_WIDTH,
height: CANVAS_HEIGHT - 20,
fitView: true,
modes: {
default: ['drag-node', 'drag-canvas', 'zoom-canvas'],
},
});
const data = generateData();
graph.data(data);
graph.render();
const aspectRatio = CANVAS_HEIGHT / CANVAS_WIDTH;
layoutConfigs.map((config, i) => {
const layoutContainer = containers[i];
const layoutGraph = new G6.Graph({
container: layoutContainer.current as string | HTMLElement,
width: 280,
height: aspectRatio * 280,
minZoom: 0.0001,
fitView: config.type !== 'force',
layout: config,
});
config.type === 'force' && layoutGraph.on('afterlayout', e => {
graph.fitView();
});
const cData = Object.assign({}, data);
layoutGraph.data(cData)
layoutGraph.render();
})
});
let describe = `Recommended Layout: ${recommendedLayout}.`;
return (
<div style={{ display: 'inline-flex', width: '100%', height: '100vh' }}>
<div className='left-container' style={{ width: '75%', padding: '8px', border: 'solid 1px #000' }}>
<div ref={container} style={{ height: '90%', border: 'solid 1px #000' }}></div>
<div className='description' style={{ textAlign: 'center', marginTop: '8px', lineHeight: '50px', height: '50px', border: 'solid 1px #000' }}>{describe}</div>
</div>
<div className='right-container' style={{ width: '25%', border: 'solid 1px #000', overflow: 'scroll' }}>
{containerDivs}
</div>
</div>);
};
export default AutoLayout;

View File

@ -9,6 +9,7 @@ import ChangeData from './component/changeData';
import ComboForceLayout from './component/combo-force-layout';
import ForceLayout from './component/force-layout';
import CompactBox from './component/compact-box';
import AutoLayout from './component/auto-layout';
export default { title: 'Layout' };
@ -21,4 +22,5 @@ storiesOf('Layout', module)
.add('combo force layout', () => <ComboForceLayout />)
.add('Fruchterman worker layout', () => <FruchtermanWorker />)
.add('force layout', () => <ForceLayout />)
.add('compactbox layout', () => <CompactBox />);
.add('compactbox layout', () => <CompactBox />)
.add('auto layout', () => <AutoLayout />)