功能包含:
- 鼠标滚动放大缩小
- node节点click、hover、color设置
- edge连线hover
- 动态添加节点、连线
- 快捷键绑定
- tip
-
底部有惊喜呦
<svg id="demo" width="800" height="800" />
import * as d3 from 'd3';
import dagreD3 from 'dagre-d3';
import d3Tip from './d3-tip';
let data = [
{
nodeId: 21,
nodeName: 'demo21',
color: red,
edges: [
{
label: 'demo22222222211111111111',
nodeId: 22,
nodeName: 'demo22',
color: black,
}
]
}
];
data.forEach(function(state) {
//创建节点
g.setNode(state.nodeId, { label: state.nodeName, text: state.nodeName });
//填充样式:颜色
g.node(state.nodeId).style = 'fill: ' + state.color;
// 创建连线
state.edges.forEach(edge => {
//创建相关节点,可自己实际情况
g.setNode(edge.nodeId, { label: edge.nodeName text: edge.nodeName });
g.node(edge.nodeId).style = 'fill: ' + edge.color;
//创建连线
g.setEdge(state.nodeId, edge.nodeId, { label: '', text: edge.label });
})
});
//设置基础样式
g.nodes().forEach(function(v) {
var node = g.node(v);
node.rx = node.ry = 5;
});
//获取svg 语法jq选择器
var svg = d3.select('#' + id),
//要注意这里是append
inner = svg.append('g');
// 鼠标滚动放大缩小
var zoom = d3.zoom().on('zoom', function() {
inner.attr('transform', d3.event.transform);
});
svg.call(zoom);
// 开始渲染
var render = new dagreD3.render();
render(inner, g);
//实现Tip
const tip = d3Tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(c => c);
svg.call(tip);
//初始化 缩放比例
var initialScale = 1;
//设置宽高
svg.call(
zoom.transform,
d3.zoomIdentity
.translate((svg.attr('width') - g.graph().width * initialScale) / 2, 20)
.scale(initialScale)
);
svg.attr('height', svg.attr('height') * initialScale + 40);
//获取node
var selectionsNode = inner.selectAll('g.node');
let that = this;
selectionsNode
//click 点击事件
.on('click', function(d) {
const { metaKey, ctrlKey } = d3.event;
// 兼容外接键盘 绑定快捷键
if (metaKey || ctrlKey) {
// let curNode = g.node(d);
} else {
//balala
that.getMoreNode( render, inner, g);
}
})
//模拟hover
.on('mouseover', function(d) {
let curNode = g.node(d);
tip.show(curNode.text);
})
.on('mouseout', function(d) {
tip.hide(d3.event);
});
var selectionsEdge = inner.selectAll('g.edgePath');
selectionsEdge
.on('mouseover', function(d) {
let curEdge = g.edge(d);
tip.show(curEdge.text);
})
.on('mouseout', function(d) {
tip.hide(d3.event);
});
getMoreNode(render, inner, g){
render(inner, g);
}
获取node节点信息
let curNode = g.node(d);
获取edge节点信息
let curEdge = g.edge(d);
异步渲染
render(inner, g);
附录:d3-tip.js
// d3.tip
// Copyright (c) 2013 Justin Palmer
//
// Tooltips for d3.js SVG visualizations
const d3 = require('d3');
const factory = (d3) => {
// Public - contructs a new tooltip
//
// Returns a tip
return function () {
let direction = d3_tip_direction,
offset = d3_tip_offset,
html = d3_tip_html,
node = initNode(),
svg = null,
point = null,
target = null;
function tip(vis) {
svg = getSVGNode(vis);
point = svg.createSVGPoint();
document.body.appendChild(node);
}
// Public - show the tooltip on the screen
//
// Returns a tip
tip.show = function () {
const args = Array.prototype.slice.call(arguments);
if (args[args.length - 1] instanceof SVGElement) target = args.pop();
let content = html.apply(this, args),
poffset = offset.apply(this, args),
dir = direction.apply(this, args),
nodel = getNodeEl(),
i = directions.length,
coords,
scrollTop = document.documentElement.scrollTop || document.body.scrollTop,
scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
nodel.html(content)
.style('opacity', 1).style('pointer-events', 'all');
while (i--) nodel.classed(directions[i], false);
coords = direction_callbacks.get(dir).apply(this);
nodel.classed(dir, true)
.style('top', `${(coords.top + poffset[0]) + scrollTop}px`)
.style('left', `${(coords.left + poffset[1]) + scrollLeft}px`);
return tip;
};
// Public - hide the tooltip
//
// Returns a tip
tip.hide = function () {
const nodel = getNodeEl();
nodel.style('opacity', 0).style('pointer-events', 'none');
return tip;
};
// Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value.
//
// n - name of the attribute
// v - value of the attribute
//
// Returns tip or attribute value
tip.attr = function (n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return getNodeEl().attr(n);
} else {
const args = Array.prototype.slice.call(arguments);
d3.selection.prototype.attr.apply(getNodeEl(), args);
}
return tip;
};
// Public: Proxy style calls to the d3 tip container. Sets or gets a style value.
//
// n - name of the property
// v - value of the property
//
// Returns tip or style property value
tip.style = function (n, v) {
if (arguments.length < 2 && typeof n === 'string') {
return getNodeEl().style(n);
} else {
const args = Array.prototype.slice.call(arguments);
d3.selection.prototype.style.apply(getNodeEl(), args);
}
return tip;
};
// Public: Set or get the direction of the tooltip
//
// v - One of n(north), s(south), e(east), or w(west), nw(northwest),
// sw(southwest), ne(northeast) or se(southeast)
//
// Returns tip or direction
tip.direction = function (v) {
if (!arguments.length) return direction;
direction = v == null ? v : functor(v);
return tip;
};
// Public: Sets or gets the offset of the tip
//
// v - Array of [x, y] offset
//
// Returns offset or
tip.offset = function (v) {
if (!arguments.length) return offset;
offset = v == null ? v : functor(v);
return tip;
};
// Public: sets or gets the html value of the tooltip
//
// v - String value of the tip
//
// Returns html value or tip
tip.html = function (v) {
if (!arguments.length) return html;
html = v == null ? v : functor(v);
return tip;
};
// Public: destroys the tooltip and removes it from the DOM
//
// Returns a tip
tip.destroy = function () {
if (node) {
getNodeEl().remove();
node = null;
}
return tip;
};
function d3_tip_direction() { return 'n'; }
function d3_tip_offset() { return [0, 0]; }
function d3_tip_html() { return ' '; }
var direction_callbacks = d3.map({
n: direction_n,
s: direction_s,
e: direction_e,
w: direction_w,
nw: direction_nw,
ne: direction_ne,
sw: direction_sw,
se: direction_se,
}),
directions = direction_callbacks.keys();
function direction_n() {
const bbox = getScreenBBox();
return {
top: bbox.n.y - node.offsetHeight,
left: bbox.n.x - node.offsetWidth / 2,
};
}
function direction_s() {
const bbox = getScreenBBox();
return {
top: bbox.s.y,
left: bbox.s.x - node.offsetWidth / 2,
};
}
function direction_e() {
const bbox = getScreenBBox();
return {
top: bbox.e.y - node.offsetHeight / 2,
left: bbox.e.x,
};
}
function direction_w() {
const bbox = getScreenBBox();
return {
top: bbox.w.y - node.offsetHeight / 2,
left: bbox.w.x - node.offsetWidth,
};
}
function direction_nw() {
const bbox = getScreenBBox();
return {
top: bbox.nw.y - node.offsetHeight,
left: bbox.nw.x - node.offsetWidth,
};
}
function direction_ne() {
const bbox = getScreenBBox();
return {
top: bbox.ne.y - node.offsetHeight,
left: bbox.ne.x,
};
}
function direction_sw() {
const bbox = getScreenBBox();
return {
top: bbox.sw.y,
left: bbox.sw.x - node.offsetWidth,
};
}
function direction_se() {
const bbox = getScreenBBox();
return {
top: bbox.se.y,
left: bbox.e.x,
};
}
function initNode() {
const node = d3.select(document.createElement('div'));
node.style('position', 'absolute').style('top', 0).style('opacity', 0)
.style('pointer-events', 'none')
.style('box-sizing', 'border-box');
return node.node();
}
function getSVGNode(el) {
el = el.node();
if (el.tagName.toLowerCase() === 'svg') { return el; }
return el.ownerSVGElement;
}
function getNodeEl() {
if (node === null) {
node = initNode();
// re-add node to DOM
document.body.appendChild(node);
}
return d3.select(node);
}
// Private - gets the screen coordinates of a shape
//
// Given a shape on the screen, will return an SVGPoint for the directions
// n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest),
// sw(southwest).
//
// +-+-+
// | |
// + +
// | |
// +-+-+
//
// Returns an Object {n, s, e, w, nw, sw, ne, se}
function getScreenBBox() {
let targetel = target || d3.event.target;
while (typeof targetel.getScreenCTM === 'undefined' && targetel.parentNode === 'undefined') {
targetel = targetel.parentNode;
}
let bbox = {},
matrix = targetel.getScreenCTM(),
tbbox = targetel.getBBox(),
width = tbbox.width,
height = tbbox.height,
x = tbbox.x,
y = tbbox.y;
point.x = x;
point.y = y;
bbox.nw = point.matrixTransform(matrix);
point.x += width;
bbox.ne = point.matrixTransform(matrix);
point.y += height;
bbox.se = point.matrixTransform(matrix);
point.x -= width;
bbox.sw = point.matrixTransform(matrix);
point.y -= height / 2;
bbox.w = point.matrixTransform(matrix);
point.x += width;
bbox.e = point.matrixTransform(matrix);
point.x -= width / 2;
point.y -= height / 2;
bbox.n = point.matrixTransform(matrix);
point.y += height;
bbox.s = point.matrixTransform(matrix);
return bbox;
}
// Private - replace D3JS 3.X d3.functor() function
function functor(v) {
return typeof v === 'function' ? v : function () {
return v;
};
}
return tip;
};
};
module.exports = factory(d3);
css
.d3-tip {
line-height: 1;
padding: 6px;
background: #fff;
color: #333;
font-size: 14px;
// font-weight: bolder;
border-radius: 2px;
border: 1px solid #eee;
z-index: 9999;
}
text {
font-weight: 300;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serf;
font-size: 14px;
}
.node rect {
stroke: #333;
fill: #fff;
}
.edgePath path {
stroke: #333;
fill: #333;
stroke-width: 1.5px;
}
网友评论