feat(behavior): add behavior activate-relations

This commit is contained in:
yilin.qyl 2019-03-28 18:00:04 +08:00
parent a5e0a9b66f
commit 5099c57d85
4 changed files with 267 additions and 24 deletions

View File

@ -0,0 +1,78 @@
module.exports = {
getDefaultCfg() {
return {
trigger: 'mouseenter', // 可选 mouseenter || click
activeState: 'active',
inactiveState: 'inactive',
shouldUpdate() { return true; }
};
},
getEvents() {
if (this.get('trigger') === 'mouseenter') {
return {
'node:mouseenter': 'setAllItemStates',
'node:mouseleave': 'clearAllItemStates'
};
}
return {
'node:click': 'setAllItemStates',
'canvas:click': 'clearAllItemStates'
};
},
setAllItemStates(e) {
const graph = this.get('graph');
const item = e.item;
this.item = item;
if (!this.shouldUpdate(e.item, { event: e, action: 'activate' })) {
return;
}
const activeState = this.get('activeState');
const inactiveState = this.get('inactiveState');
graph.setAutoPaint(false);
graph.getNodes().forEach(function(node) {
graph.setItemState(node, activeState, false);
inactiveState && graph.setItemState(node, inactiveState, true);
});
graph.getEdges().forEach(function(edge) {
graph.setItemState(edge, activeState, false);
inactiveState && graph.setItemState(edge, inactiveState, true);
});
inactiveState && graph.setItemState(item, inactiveState, false);
graph.setItemState(item, activeState, true);
graph.getEdges().forEach(function(edge) {
if (edge.getSource() === item) {
inactiveState && graph.setItemState(edge.getTarget(), inactiveState, false);
graph.setItemState(edge.getTarget(), activeState, true);
graph.setItemState(edge, activeState, true);
graph.setItemState(edge, inactiveState, false);
edge.toFront();
} else if (edge.getTarget() === item) {
inactiveState && graph.setItemState(edge.getSource(), inactiveState, false);
graph.setItemState(edge.getSource(), activeState, true);
graph.setItemState(edge, activeState, true);
graph.setItemState(edge, inactiveState, false);
edge.toFront();
}
});
graph.paint();
graph.setAutoPaint(true);
graph.emit('afteractivaterelations', { item: e.item, action: 'activate' });
},
clearAllItemStates(e) {
const graph = this.get('graph');
if (!this.shouldUpdate(e.item, { event: e, action: 'deactivate' })) {
return;
}
const autoPaint = graph.get('autoPaint');
graph.setAutoPaint(false);
graph.getNodes().forEach(function(node) {
graph.clearItemStates(node);
});
graph.getEdges().forEach(function(edge) {
graph.clearItemStates(edge);
});
graph.paint();
graph.setAutoPaint(autoPaint);
graph.emit('afteractivaterelations', { item: e.item || this.item, action: 'deactivate' });
}
};

View File

@ -7,7 +7,8 @@ const behaviors = {
'click-select': require('./click-select'),
tooltip: require('./tooltip'),
'edge-tooltip': require('./edge-tooltip'),
'collapse-expand': require('./collapse-expand')
'collapse-expand': require('./collapse-expand'),
'activate-relations': require('./activate-relations')
};
Util.each(behaviors, (behavior, type) => {
Behavior.registerBehavior(type, behavior);

View File

@ -224,8 +224,8 @@ class Graph extends EventEmitter {
_initPlugins() {
const self = this;
Util.each(self.get('plugins'), plugin => {
if (!plugin.destroyed && plugin.init) {
plugin.init(self);
if (!plugin.destroyed && plugin._init) {
plugin._init(self);
}
});
}
@ -516,31 +516,15 @@ class Graph extends EventEmitter {
return this;
}
/**
* 改变画布大小
* @private 仅供内部更新视口使用
* @param {array} matrix group矩阵
*/
_updateMatrix(matrix) {
const rootGroup = this.get('group');
const minZoom = this.get('minZoom');
const maxZoom = this.get('maxZoom');
if (minZoom && matrix[0] < minZoom) {
return;
}
if (maxZoom && matrix[0] > maxZoom) {
return;
}
rootGroup.setMatrix(matrix);
}
/**
* 平移画布
* @param {number} dx 水平方向位移
* @param {number} dy 垂直方向位移
*/
translate(dx, dy) {
this.get('group').translate(dx, dy);
const group = this.get('group');
group.translate(dx, dy);
this.emit('viewportchange', { action: 'translate', matrix: group.getMatrix() });
this.autoPaint();
}
@ -550,7 +534,10 @@ class Graph extends EventEmitter {
* @param {number} y 垂直坐标
*/
moveTo(x, y) {
this.get('group').move(x, y);
const group = this.get('group');
group.move(x, y);
this.emit('viewportchange', { action: 'move', matrix: group.getMatrix() });
this.autoPaint();
}
/**
@ -637,6 +624,8 @@ class Graph extends EventEmitter {
*/
zoom(ratio, center) {
const matrix = Util.clone(this.get('group').getMatrix());
const minZoom = this.get('minZoom');
const maxZoom = this.get('maxZoom');
if (center) {
Util.mat3.translate(matrix, matrix, [ -center.x, -center.y ]);
Util.mat3.scale(matrix, matrix, [ ratio, ratio ]);
@ -644,7 +633,14 @@ class Graph extends EventEmitter {
} else {
Util.mat3.scale(matrix, matrix, [ ratio, ratio ]);
}
this._updateMatrix(matrix);
if (minZoom && matrix[0] < minZoom) {
return;
}
if (maxZoom && matrix[0] > maxZoom) {
return;
}
this.get('group').setMatrix(matrix);
this.emit('viewportchange', { action: 'zoom', matrix });
this.autoPaint();
}

View File

@ -0,0 +1,168 @@
const expect = require('chai').expect;
const G6 = require('../../../src');
describe('activate-relations', () => {
const div = document.createElement('div');
div.id = 'activate-relations-spec';
document.body.appendChild(div);
const graph = new G6.Graph({
container: div,
width: 500,
height: 500,
pixelRatio: 2,
modes: { default: [] }
});
const node1 = graph.addItem('node', { id: 'node1', x: 100, y: 100, label: 'node1' });
const node2 = graph.addItem('node', { id: 'node2', x: 200, y: 200, label: 'node2' });
graph.addItem('node', { id: 'node3', x: 80, y: 150, label: 'node3' });
graph.addItem('edge', { source: 'node1', target: 'node2' });
graph.addItem('edge', { source: 'node1', target: 'node3' });
it('default activate', done => {
graph.on('afteractivaterelations', e => {
const action = e.action;
if (e.item === node1) {
if (action === 'activate') {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(3);
expect(edges.length).to.equal(2);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
const keyShape = node2.getKeyShape();
expect(keyShape.attr('fillOpacity')).to.equal(0.8);
} else {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(0);
expect(edges.length).to.equal(0);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
}
} else {
if (action === 'activate') {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(2);
expect(edges.length).to.equal(1);
expect(graph.findAllByState('node', 'inactive').length).to.equal(1);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(1);
const keyShape = node1.getKeyShape();
expect(keyShape.attr('fillOpacity')).to.equal(0.8);
} else {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(0);
expect(edges.length).to.equal(0);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
done();
}
}
});
graph.addBehaviors([ 'activate-relations' ], 'default');
graph.emit('node:mouseenter', { item: node1 });
graph.emit('node:mouseleave', { item: node1 });
graph.emit('node:mouseenter', { item: node2 });
graph.emit('node:mouseleave', { item: node2 });
graph.removeBehaviors([ 'activate-relations' ], 'default');
graph.removeEvent();
});
it('click to activate', done => {
graph.on('afteractivaterelations', e => {
const action = e.action;
if (e.item === node1) {
if (action === 'activate') {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(3);
expect(edges.length).to.equal(2);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
const keyShape = node2.getKeyShape();
expect(keyShape.attr('fillOpacity')).to.equal(0.8);
} else {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(0);
expect(edges.length).to.equal(0);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
}
} else {
if (action === 'activate') {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(2);
expect(edges.length).to.equal(1);
expect(graph.findAllByState('node', 'inactive').length).to.equal(1);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(1);
const keyShape = node1.getKeyShape();
expect(keyShape.attr('fillOpacity')).to.equal(0.8);
} else {
const nodes = graph.findAllByState('node', 'active');
const edges = graph.findAllByState('edge', 'active');
expect(nodes.length).to.equal(0);
expect(edges.length).to.equal(0);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
done();
}
}
});
graph.addBehaviors([{
type: 'activate-relations',
trigger: 'click'
}], 'default');
graph.emit('node:click', { item: node1 });
graph.emit('canvas:click', {});
graph.emit('node:click', { item: node2 });
graph.emit('canvas:click', {});
graph.removeBehaviors([ 'activate-relations' ], 'default');
graph.removeEvent();
});
it('custom state', done => {
graph.on('afteractivaterelations', e => {
const action = e.action;
if (e.item === node1) {
if (action === 'activate') {
const nodes = graph.findAllByState('node', 'highlight');
const edges = graph.findAllByState('edge', 'highlight');
expect(nodes.length).to.equal(3);
expect(edges.length).to.equal(2);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
} else {
const nodes = graph.findAllByState('node', 'highlight');
const edges = graph.findAllByState('edge', 'highlight');
expect(nodes.length).to.equal(0);
expect(edges.length).to.equal(0);
}
} else {
if (action === 'activate') {
const nodes = graph.findAllByState('node', 'highlight');
const edges = graph.findAllByState('edge', 'highlight');
expect(nodes.length).to.equal(2);
expect(edges.length).to.equal(1);
expect(graph.findAllByState('node', 'inactive').length).to.equal(0);
expect(graph.findAllByState('edge', 'inactive').length).to.equal(0);
} else {
const nodes = graph.findAllByState('node', 'highlight');
const edges = graph.findAllByState('edge', 'highlight');
expect(nodes.length).to.equal(0);
expect(edges.length).to.equal(0);
done();
}
}
});
graph.addBehaviors([{
type: 'activate-relations',
activeState: 'highlight',
inactiveState: null
}], 'default');
graph.emit('node:mouseenter', { item: node1 });
graph.emit('node:mouseleave', { item: node1 });
graph.emit('node:mouseenter', { item: node2 });
graph.emit('node:mouseleave', { item: node2 });
graph.destroy();
});
});