mirror of
https://gitee.com/antv/g6.git
synced 2024-12-01 03:08:33 +08:00
feat: fisheye with dragging. feat: fisheye with scaling range and d.
This commit is contained in:
parent
0cdc30fe6a
commit
e3a9ba0ec3
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* mousemove、click、drag 事件的响应函数
|
||||
* @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);
|
||||
|
@ -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();
|
||||
|
131
stories/Layout/component/auto-layout.tsx
Normal file
131
stories/Layout/component/auto-layout.tsx
Normal 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;
|
@ -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 />)
|
||||
|
Loading…
Reference in New Issue
Block a user