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 Graph from '../../graph/graph';
|
||||||
import Base from '../base';
|
import Base from '../base';
|
||||||
|
|
||||||
|
const DELTA = 0.05;
|
||||||
|
|
||||||
interface FisheyeConfig {
|
interface FisheyeConfig {
|
||||||
trigger?: 'mousemove' | 'click';
|
trigger?: 'mousemove' | 'click' | 'drag';
|
||||||
d?: number;
|
d?: number;
|
||||||
r?: number;
|
r?: number;
|
||||||
delegateStyle?: ShapeStyle;
|
delegateStyle?: ShapeStyle;
|
||||||
showLabel?: boolean;
|
showLabel?: boolean;
|
||||||
|
wheelScaleRange?: boolean;
|
||||||
|
maxR?: number;
|
||||||
|
minR?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lensDelegateStyle = {
|
const lensDelegateStyle = {
|
||||||
@ -31,14 +36,25 @@ export default class Fisheye extends Base {
|
|||||||
|
|
||||||
// class-methods-use-this
|
// class-methods-use-this
|
||||||
public getEvents() {
|
public getEvents() {
|
||||||
if ((this as any).get('trigger') === 'mousemove') {
|
let events;
|
||||||
return {
|
switch ((this as any).get('trigger')) {
|
||||||
mousemove: 'magnify',
|
case 'click':
|
||||||
};
|
events = {
|
||||||
|
click: 'magnify',
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'drag':
|
||||||
|
events = {
|
||||||
|
click: 'createDelegate',
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
events = {
|
||||||
|
mousemove: 'magnify',
|
||||||
|
};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return {
|
return events;
|
||||||
click: 'magnify',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
@ -49,11 +65,61 @@ export default class Fisheye extends Base {
|
|||||||
self.set('r2', r * r);
|
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;
|
const self = this;
|
||||||
self.restoreCache();
|
self.restoreCache();
|
||||||
const graph: Graph = self.get('graph');
|
const graph: Graph = self.get('graph');
|
||||||
@ -65,7 +131,15 @@ export default class Fisheye extends Base {
|
|||||||
const d = self.get('d');
|
const d = self.get('d');
|
||||||
const nodes = graph.getNodes();
|
const nodes = graph.getNodes();
|
||||||
const nodeLength = nodes.length;
|
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);
|
self.updateDelegate(mCenter, r);
|
||||||
for (let i = 0; i < nodeLength; i++) {
|
for (let i = 0; i < nodeLength; i++) {
|
||||||
const model = nodes[i].getModel();
|
const model = nodes[i].getModel();
|
||||||
@ -127,6 +201,12 @@ export default class Fisheye extends Base {
|
|||||||
self.set('cachedOriginPositions', {});
|
self.set('cachedOriginPositions', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供给用户调整系数
|
||||||
|
* @param {Point} mCenter
|
||||||
|
* @param {number} r
|
||||||
|
*/
|
||||||
public updateParams(cfg: FisheyeConfig) {
|
public updateParams(cfg: FisheyeConfig) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const { r, d, trigger } = cfg;
|
const { r, d, trigger } = cfg;
|
||||||
@ -137,7 +217,7 @@ export default class Fisheye extends Base {
|
|||||||
if (!isNaN(d)) {
|
if (!isNaN(d)) {
|
||||||
self.set('d', d);
|
self.set('d', d);
|
||||||
}
|
}
|
||||||
if (trigger === 'mousemove' || trigger === 'click') {
|
if (trigger === 'mousemove' || trigger === 'click' || trigger === 'drag') {
|
||||||
self.set('trigger', trigger);
|
self.set('trigger', trigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,13 +244,41 @@ export default class Fisheye extends Base {
|
|||||||
y: mCenter.y,
|
y: mCenter.y,
|
||||||
...attrs,
|
...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 {
|
} else {
|
||||||
lensDelegate.attr({
|
lensDelegate.attr({
|
||||||
x: mCenter.x,
|
x: mCenter.x,
|
||||||
y: mCenter.y,
|
y: mCenter.y,
|
||||||
|
r: r / 1.5
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.set('delegate', lensDelegate);
|
self.set('delegate', lensDelegate);
|
||||||
|
@ -9,6 +9,8 @@ const FishEye = () => {
|
|||||||
const fisheye = new G6.Fisheye({
|
const fisheye = new G6.Fisheye({
|
||||||
r: 200,
|
r: 200,
|
||||||
showLabel: true,
|
showLabel: true,
|
||||||
|
trigger: 'drag',
|
||||||
|
wheelScaleRange: true
|
||||||
});
|
});
|
||||||
const colors = [
|
const colors = [
|
||||||
'#8FE9FF',
|
'#8FE9FF',
|
||||||
@ -27,7 +29,7 @@ const FishEye = () => {
|
|||||||
graph = new G6.Graph({
|
graph = new G6.Graph({
|
||||||
container: container.current as string | HTMLElement,
|
container: container.current as string | HTMLElement,
|
||||||
width: 1000,
|
width: 1000,
|
||||||
height: 700,
|
height: 600,
|
||||||
plugins: [fisheye],
|
plugins: [fisheye],
|
||||||
layout: {
|
layout: {
|
||||||
type: 'force',
|
type: 'force',
|
||||||
@ -47,7 +49,6 @@ const FishEye = () => {
|
|||||||
fill: colors[Math.floor(Math.random() * 9)],
|
fill: colors[Math.floor(Math.random() * 9)],
|
||||||
lineWidth: 0,
|
lineWidth: 0,
|
||||||
};
|
};
|
||||||
console.log(node);
|
|
||||||
});
|
});
|
||||||
graph.data(data);
|
graph.data(data);
|
||||||
graph.render();
|
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 ComboForceLayout from './component/combo-force-layout';
|
||||||
import ForceLayout from './component/force-layout';
|
import ForceLayout from './component/force-layout';
|
||||||
import CompactBox from './component/compact-box';
|
import CompactBox from './component/compact-box';
|
||||||
|
import AutoLayout from './component/auto-layout';
|
||||||
|
|
||||||
export default { title: 'Layout' };
|
export default { title: 'Layout' };
|
||||||
|
|
||||||
@ -21,4 +22,5 @@ storiesOf('Layout', module)
|
|||||||
.add('combo force layout', () => <ComboForceLayout />)
|
.add('combo force layout', () => <ComboForceLayout />)
|
||||||
.add('Fruchterman worker layout', () => <FruchtermanWorker />)
|
.add('Fruchterman worker layout', () => <FruchtermanWorker />)
|
||||||
.add('force layout', () => <ForceLayout />)
|
.add('force layout', () => <ForceLayout />)
|
||||||
.add('compactbox layout', () => <CompactBox />);
|
.add('compactbox layout', () => <CompactBox />)
|
||||||
|
.add('auto layout', () => <AutoLayout />)
|
||||||
|
Loading…
Reference in New Issue
Block a user