问题描述
我想在链接中间放置一个标记,而不是像我的代码那样将它放在最后。
I want to place a marker in the middle of the links instead of placing it at the end as is done by my code.
尽管我使用Google搜索,但我我无法为我的代码找到解决方案。
Although I googled it, I am not able to find a solution for my code.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="../D3/d3.min.js"></script>
</head>
<body>
<style>
body {
background-color: #3a5795;
}
svg:not(.active):not(.ctrl) {
cursor: crosshair;
}
path.link {
fill: none;
stroke:floralwhite;
stroke-width: 4px;
cursor: default;
}
svg:not(.active):not(.ctrl) path.link {
cursor: pointer;
}
path.link.selected {
stroke-dasharray: 10,2;
}
path.link.dragline {
pointer-events: none;
}
path.link.hidden {
stroke-width: 0;
}
rect.node {
stroke-width: 1.5px;
cursor: pointer;
}
rect.node.reflexive {
stroke: #000 !important;
stroke-width: 2.5px;
}
text {
font: 12px sans-serif;
pointer-events: none;
}
text.id {
text-anchor: middle;
font-weight: bold;
}
</style>
<script type="text/javascript">
// set up SVG for D3
var width = 1400,
height = 800,
colors = d3.scale.category10();
var svg = d3.select('body')
.append('svg')
.attr('oncontextmenu', 'return false;')
.attr('width', width)
.attr('height', height);
// set up initial nodes and links
// - nodes are known by 'id', not by index in array.
// - reflexive edges are indicated on the node (as a bold black rect).
// - links are always source < target; edge directions are set by 'left' and 'right'.
var nodes = [
{
"id": "Component",
"description": "Component are the Containers",
"type":"wiring"
},
{
"id": "Form Design And Data Design",
"description": "In the Form Design and Data Design we can create form and data",
"type": "wiring"
},
{
"id": "Data and Property ",
"description": "All the Data has the Property and value Associated with It",
"type":"wiring"
},
{
"id": "Entity Query",
"description": "Entity Queries can be used to create Entity Relationship ",
"type": "wiring"
},
{
"id": "Entity Query and Entity Data",
"description": "Entity Data Can be used to create ",
"type": "wiring"
}
],
lastNodeId = 2,
links = [
];
// init D3 force layout
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(250)
.charge(-1000)
.gravity(0.05)
.on('tick', tick)
//define arrow markers for graph links
svg.append('svg:defs').append('svg:marker')
.attr('id', 'end-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 6)
.attr('markerWidth', 3)
.attr('markerHeight', 3)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('fill', '#000');
svg.append('svg:defs').append('svg:marker')
.attr('id', 'start-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 4)
.attr('markerWidth', 6)
.attr('markerHeight', 5)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M10,-5L0,0L10,5')
.attr('fill', '#000');
// line displayed when dragging new nodes
var drag_line = svg.append('svg:path')
.attr('class', 'link dragline hidden')
.attr('d', 'M0,0L0,0');
// handles to link and node element groups
var path = svg.append('svg:g').selectAll('path'),
rect = svg.append('svg:g').selectAll('g');
// mouse event vars
var selected_node = null,
selected_link = null,
mousedown_link = null,
mousedown_node = null,
mouseup_node = null;
function wrapText(text, width) {
text.each(function () {
var textEl = d3.select(this),
words = textEl.text().split(/\s+/).reverse(),
word,
line = [],
linenumber = 0,
lineHeight = 1.1, // ems
y = textEl.attr('y'),
dx = parseFloat(textEl.attr('dx') || 0),
dy = parseFloat(textEl.attr('dy') || 0),
tspan = textEl.text(null).append('tspan').attr('x', 0).attr('y', y).attr('dy', dy + 'em');
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(' '));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(' '));
line = [word];
tspan = textEl.append('tspan').attr('x', 0).attr('y', y).attr('dx', dx).attr('dy', ++linenumber * lineHeight + dy + 'em').text(word);
}
}
});
}
function resetMouseVars() {
mousedown_node = null;
mouseup_node = null;
mousedown_link = null;
}
// update force layout (called automatically each iteration)
function tick() {
// draw directed edges with proper padding from node centers
path.attr('d', function (d) {
var deltaX = d.target.x - d.source.x,
deltaY = d.target.y - d.source.y,
dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
normX = deltaX / dist,
normY = deltaY / dist,
sourcePadding = d.left ? 17 : 12,
targetPadding = d.right ? 17 : 12,
sourceX = d.source.x + (sourcePadding * normX),
sourceY = d.source.y + (sourcePadding * normY),
targetX = d.target.x - (targetPadding * normX),
targetY = d.target.y - (targetPadding * normY);
return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
});
rect.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
}
// update graph (called when needed)
function restart() {
// path (link) group
path = path.data(links);
// update existing links
path.classed('selected', function (d) { return d === selected_link; })
.style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
.style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; });
// add new links
path.enter().append('svg:path')
.attr('class', 'link')
.classed('selected', function (d) { return d === selected_link; })
.style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
.style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; })
.on('mousedown', function (d) {
if (d3.event.ctrlKey) return;
// select link
mousedown_link = d;
if (mousedown_link === selected_link) selected_link = null;
else selected_link = mousedown_link;
selected_node = null;
restart();
});
// remove old links
path.exit().remove();
// rect (node) group
// NB: the function arg is crucial here! nodes are known by id, not by index!
rect = rect.data(nodes, function (d) { return d.id; });
// update existing nodes (reflexive & selected visual states)
rect.selectAll('rect')
.style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
.classed('reflexive', function (d) { return d.reflexive; });
// add new nodes
var g = rect.enter().append('svg:g');
//g.append('svg:rect')
// .attr('class', 'node')
// .attr('r', 30)
g.append('svg:rect')
.attr('class', 'node')
.attr('width', 150)
.attr("height", 60)
.attr("rx", 30)
.attr("ry", 30)
.attr("x", -75)
.attr("y", -16.5)
.style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
.style('stroke', function (d) { return d3.rgb(colors(d.id)).darker().toString(); })
.classed('reflexive', function (d) { return d.reflexive; })
.on('mouseover', function (d) {
if (!mousedown_node || d === mousedown_node) return;
// enlarge target node
d3.select(this).attr('transform', 'scale(1.1)');
})
.on('mouseout', function (d) {
if (!mousedown_node || d === mousedown_node) return;
// unenlarge target node
d3.select(this).attr('transform', '');
})
.on('mousedown', function (d) {
if (d3.event.ctrlKey) return;
// select node
mousedown_node = d;
if (mousedown_node === selected_node) selected_node = null;
else selected_node = mousedown_node;
selected_link = null;
// reposition drag line
drag_line
.style('marker-end', 'url(#end-arrow)')
.classed('hidden', false)
.attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + mousedown_node.x + ',' + mousedown_node.y);
restart();
})
.on('mouseup', function (d) {
if (!mousedown_node) return;
// needed by FF
drag_line
.classed('hidden', true)
.style('marker-end', '');
// check for drag-to-self
mouseup_node = d;
if (mouseup_node === mousedown_node) { resetMouseVars(); return; }
// unenlarge target node
d3.select(this).attr('transform', '');
// add link to graph (update if exists)
// NB: links are strictly source < target; arrows separately specified by booleans
var source, target, direction;
if (mousedown_node.id < mouseup_node.id) {
source = mousedown_node;
target = mouseup_node;
direction = 'right';
} else {
source = mouseup_node;
target = mousedown_node;
direction = 'left';
}
var link;
link = links.filter(function (l) {
return (l.source === source && l.target === target);
})[0];
if (link) {
link[direction] = true;
} else {
link = { source: source, target: target, left: false, right: false };
link[direction] = true;
links.push(link);
}
// select new link
selected_link = link;
selected_node = null;
restart();
});
// show node IDs
g.append('svg:text')
.attr('x', 0)
.attr('y', 4)
.attr('class', 'id')
.text(function (d) { return d.id; })
.call(wrapText, 100);
// remove old nodes
rect.exit().remove();
// set the graph in motion
force.start();
}
function mousedown() {
// prevent I-bar on drag
//d3.event.preventDefault();
// because :active only works in WebKit?
svg.classed('active', true);
if (d3.event.ctrlKey || mousedown_node || mousedown_link) return;
// insert new node at point
var point = d3.mouse(this),
node = { id: ++lastNodeId, reflexive: false };
node.x = point[0];
node.y = point[1];
nodes.push(node);
restart();
}
function mousemove() {
if (!mousedown_node) return;
// update drag line
drag_line.attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + d3.mouse(this)[0] + ',' + d3.mouse(this)[1]);
restart();
}
function mouseup() {
if (mousedown_node) {
// hide drag line
drag_line
.classed('hidden', true)
.style('marker-end', '');
}
// because :active only works in WebKit?
svg.classed('active', false);
// clear mouse event vars
resetMouseVars();
}
function spliceLinksForNode(node) {
var toSplice = links.filter(function (l) {
return (l.source === node || l.target === node);
});
toSplice.map(function (l) {
links.splice(links.indexOf(l), 1);
});
}
// only respond once per keydown
var lastKeyDown = -1;
function keydown() {
//d3.event.preventDefault();
if (lastKeyDown !== -1) return;
lastKeyDown = d3.event.keyCode;
// ctrl
if (d3.event.keyCode === 17) {
rect.call(force.drag);
svg.classed('ctrl', true);
}
if (!selected_node && !selected_link) return;
switch (d3.event.keyCode) {
case 8: // backspace
case 46: // delete
if (selected_node) {
nodes.splice(nodes.indexOf(selected_node), 1);
spliceLinksForNode(selected_node);
} else if (selected_link) {
links.splice(links.indexOf(selected_link), 1);
}
selected_link = null;
selected_node = null;
restart();
break;
case 66: // B
if (selected_link) {
// set link direction to both left and right
selected_link.left = true;
selected_link.right = true;
}
restart();
break;
case 76: // L
if (selected_link) {
// set link direction to left only
selected_link.left = true;
selected_link.right = false;
}
restart();
break;
case 82: // R
if (selected_node) {
// toggle node reflexivity
selected_node.reflexive = !selected_node.reflexive;
} else if (selected_link) {
// set link direction to right only
selected_link.left = false;
selected_link.right = true;
}
restart();
break;
}
}
function keyup() {
lastKeyDown = -1;
// ctrl
if (d3.event.keyCode === 17) {
rect
.on('mousedown.drag', null)
.on('touchstart.drag', null);
svg.classed('ctrl', false);
}
}
// app starts here
svg.on('mousedown', mousedown)
.on('mousemove', mousemove)
.on('mouseup', mouseup);
d3.select(window)
.on('keydown', keydown)
.on('keyup', keyup);
restart();
</script>
</body>
</html>
推荐答案
要在链接的中点绘制标记可以使用,它非常类似于 marker-start
和 marker-end
,除了它插入一个标记元素在中间。
To draw markers on the mid point of your links you can use marker-mid
which works pretty much like marker-start
and marker-end
except that it inserts a marker element at the middle.
path.enter().append('svg:path')
.style('marker-mid', function (d) { return 'url(#start-arrow)'; })
出于演示目的,我在这里使用了开始箭头
,这当然可以根据您的喜好进行调整。
For demonstration purposes I have just used the start-arrow
here, which may of course be adjusted to your liking.
但是,只有在中点有一个顶点时才会绘制标记。这不适用于你的代码,因为你正在绘制一条从源到目标的直线,只定义起点和终点。另一方面,有一条直线会派上用场,因为计算中点并将直线分成两个区域相当容易,从而在中间插入一个新的顶点。您的 tick()
处理程序中已经进行了计算,给出了中间结果以帮助找到中间点:
However, the marker will only be drawn, if there is a vertex at the mid point. This is not true for your code, because you are drawing a single straight line from source to target defining only start and end points. On the other hand, having a straight line comes in handy, because it is fairly easy to calculate the mid point and split up the straight line into two segements, thereby inserting a new vertex at the middle. There are already calculations going on in your tick()
handler giving intermediate results to assist in finding the mid point:
// Coordinates of mid point on line to add new vertex.
midX = (targetX - sourceX) / 2 + sourceX;
midY = (targetY - sourceY) / 2 + sourceY;
// | v --- new vertex --- v |
return 'M' + sourceX + ',' + sourceY + 'L' + midX + ',' + midY + 'L' + targetX + ',' + targetY;
查看以下工作演示代码片段。
Have a look at the following code snippet for a working demo.
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="../D3/d3.min.js"></script>
</head>
<body>
<style>
body {
background-color: #3a5795;
}
svg:not(.active):not(.ctrl) {
cursor: crosshair;
}
path.link {
fill: none;
stroke:floralwhite;
stroke-width: 4px;
cursor: default;
}
svg:not(.active):not(.ctrl) path.link {
cursor: pointer;
}
path.link.selected {
stroke-dasharray: 10,2;
}
path.link.dragline {
pointer-events: none;
}
path.link.hidden {
stroke-width: 0;
}
rect.node {
stroke-width: 1.5px;
cursor: pointer;
}
rect.node.reflexive {
stroke: #000 !important;
stroke-width: 2.5px;
}
text {
font: 12px sans-serif;
pointer-events: none;
}
text.id {
text-anchor: middle;
font-weight: bold;
}
</style>
<script type="text/javascript">
// set up SVG for D3
var width = 1400,
height = 800,
colors = d3.scale.category10();
var svg = d3.select('body')
.append('svg')
.attr('oncontextmenu', 'return false;')
.attr('width', width)
.attr('height', height);
// set up initial nodes and links
// - nodes are known by 'id', not by index in array.
// - reflexive edges are indicated on the node (as a bold black rect).
// - links are always source < target; edge directions are set by 'left' and 'right'.
var nodes = [
{
"id": "Component",
"description": "Component are the Containers",
"type":"wiring"
},
{
"id": "Form Design And Data Design",
"description": "In the Form Design and Data Design we can create form and data",
"type": "wiring"
},
{
"id": "Data and Property ",
"description": "All the Data has the Property and value Associated with It",
"type":"wiring"
},
{
"id": "Entity Query",
"description": "Entity Queries can be used to create Entity Relationship ",
"type": "wiring"
},
{
"id": "Entity Query and Entity Data",
"description": "Entity Data Can be used to create ",
"type": "wiring"
}
],
lastNodeId = 2,
links = [
];
// init D3 force layout
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(250)
.charge(-1000)
.gravity(0.05)
.on('tick', tick)
//define arrow markers for graph links
svg.append('svg:defs').append('svg:marker')
.attr('id', 'end-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 6)
.attr('markerWidth', 3)
.attr('markerHeight', 3)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('fill', '#000');
svg.append('svg:defs').append('svg:marker')
.attr('id', 'start-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 4)
.attr('markerWidth', 6)
.attr('markerHeight', 5)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M10,-5L0,0L10,5')
.attr('fill', '#000');
// line displayed when dragging new nodes
var drag_line = svg.append('svg:path')
.attr('class', 'link dragline hidden')
.attr('d', 'M0,0L0,0');
// handles to link and node element groups
var path = svg.append('svg:g').selectAll('path'),
rect = svg.append('svg:g').selectAll('g');
// mouse event vars
var selected_node = null,
selected_link = null,
mousedown_link = null,
mousedown_node = null,
mouseup_node = null;
function wrapText(text, width) {
text.each(function () {
var textEl = d3.select(this),
words = textEl.text().split(/\s+/).reverse(),
word,
line = [],
linenumber = 0,
lineHeight = 1.1, // ems
y = textEl.attr('y'),
dx = parseFloat(textEl.attr('dx') || 0),
dy = parseFloat(textEl.attr('dy') || 0),
tspan = textEl.text(null).append('tspan').attr('x', 0).attr('y', y).attr('dy', dy + 'em');
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(' '));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(' '));
line = [word];
tspan = textEl.append('tspan').attr('x', 0).attr('y', y).attr('dx', dx).attr('dy', ++linenumber * lineHeight + dy + 'em').text(word);
}
}
});
}
function resetMouseVars() {
mousedown_node = null;
mouseup_node = null;
mousedown_link = null;
}
// update force layout (called automatically each iteration)
function tick() {
console.log(path);
// draw directed edges with proper padding from node centers
path.attr('d', function (d) {
var deltaX = d.target.x - d.source.x,
deltaY = d.target.y - d.source.y,
dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
normX = deltaX / dist,
normY = deltaY / dist,
sourcePadding = d.left ? 17 : 12,
targetPadding = d.right ? 17 : 12,
sourceX = d.source.x + (sourcePadding * normX),
sourceY = d.source.y + (sourcePadding * normY),
targetX = d.target.x - (targetPadding * normX),
targetY = d.target.y - (targetPadding * normY),
midX = (targetX - sourceX) / 2 + sourceX,
midY = (targetY - sourceY) / 2 + sourceY;
return 'M' + sourceX + ',' + sourceY + 'L' + midX + ',' + midY + 'L' + targetX + ',' + targetY;
});
rect.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
}
// update graph (called when needed)
function restart() {
// path (link) group
path = path.data(links);
// update existing links
path.classed('selected', function (d) { return d === selected_link; })
.style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
.style('marker-mid', function (d) { return 'url(#start-arrow)'; })
.style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; });
// add new links
path.enter().append('svg:path')
.attr('class', 'link')
.classed('selected', function (d) { return d === selected_link; })
.style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
.style('marker-mid', function (d) { return 'url(#start-arrow)'; })
.style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; })
.on('mousedown', function (d) {
if (d3.event.ctrlKey) return;
// select link
mousedown_link = d;
if (mousedown_link === selected_link) selected_link = null;
else selected_link = mousedown_link;
selected_node = null;
restart();
});
// remove old links
path.exit().remove();
// rect (node) group
// NB: the function arg is crucial here! nodes are known by id, not by index!
rect = rect.data(nodes, function (d) { return d.id; });
// update existing nodes (reflexive & selected visual states)
rect.selectAll('rect')
.style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
.classed('reflexive', function (d) { return d.reflexive; });
// add new nodes
var g = rect.enter().append('svg:g');
//g.append('svg:rect')
// .attr('class', 'node')
// .attr('r', 30)
g.append('svg:rect')
.attr('class', 'node')
.attr('width', 150)
.attr("height", 60)
.attr("rx", 30)
.attr("ry", 30)
.attr("x", -75)
.attr("y", -16.5)
.style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
.style('stroke', function (d) { return d3.rgb(colors(d.id)).darker().toString(); })
.classed('reflexive', function (d) { return d.reflexive; })
.on('mouseover', function (d) {
if (!mousedown_node || d === mousedown_node) return;
// enlarge target node
d3.select(this).attr('transform', 'scale(1.1)');
})
.on('mouseout', function (d) {
if (!mousedown_node || d === mousedown_node) return;
// unenlarge target node
d3.select(this).attr('transform', '');
})
.on('mousedown', function (d) {
if (d3.event.ctrlKey) return;
// select node
mousedown_node = d;
if (mousedown_node === selected_node) selected_node = null;
else selected_node = mousedown_node;
selected_link = null;
// reposition drag line
drag_line
.style('marker-end', 'url(#end-arrow)')
.classed('hidden', false)
.attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + mousedown_node.x + ',' + mousedown_node.y);
restart();
})
.on('mouseup', function (d) {
if (!mousedown_node) return;
// needed by FF
drag_line
.classed('hidden', true)
.style('marker-end', '');
// check for drag-to-self
mouseup_node = d;
if (mouseup_node === mousedown_node) { resetMouseVars(); return; }
// unenlarge target node
d3.select(this).attr('transform', '');
// add link to graph (update if exists)
// NB: links are strictly source < target; arrows separately specified by booleans
var source, target, direction;
if (mousedown_node.id < mouseup_node.id) {
source = mousedown_node;
target = mouseup_node;
direction = 'right';
} else {
source = mouseup_node;
target = mousedown_node;
direction = 'left';
}
var link;
link = links.filter(function (l) {
return (l.source === source && l.target === target);
})[0];
if (link) {
link[direction] = true;
} else {
link = { source: source, target: target, left: false, right: false };
link[direction] = true;
links.push(link);
}
// select new link
selected_link = link;
selected_node = null;
restart();
});
// show node IDs
g.append('svg:text')
.attr('x', 0)
.attr('y', 4)
.attr('class', 'id')
.text(function (d) { return d.id; })
.call(wrapText, 100);
// remove old nodes
rect.exit().remove();
// set the graph in motion
force.start();
}
function mousedown() {
// prevent I-bar on drag
//d3.event.preventDefault();
// because :active only works in WebKit?
svg.classed('active', true);
if (d3.event.ctrlKey || mousedown_node || mousedown_link) return;
// insert new node at point
var point = d3.mouse(this),
node = { id: ++lastNodeId, reflexive: false };
node.x = point[0];
node.y = point[1];
nodes.push(node);
restart();
}
function mousemove() {
if (!mousedown_node) return;
// update drag line
drag_line.attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + d3.mouse(this)[0] + ',' + d3.mouse(this)[1]);
restart();
}
function mouseup() {
if (mousedown_node) {
// hide drag line
drag_line
.classed('hidden', true)
.style('marker-end', '');
}
// because :active only works in WebKit?
svg.classed('active', false);
// clear mouse event vars
resetMouseVars();
}
function spliceLinksForNode(node) {
var toSplice = links.filter(function (l) {
return (l.source === node || l.target === node);
});
toSplice.map(function (l) {
links.splice(links.indexOf(l), 1);
});
}
// only respond once per keydown
var lastKeyDown = -1;
function keydown() {
//d3.event.preventDefault();
if (lastKeyDown !== -1) return;
lastKeyDown = d3.event.keyCode;
// ctrl
if (d3.event.keyCode === 17) {
rect.call(force.drag);
svg.classed('ctrl', true);
}
if (!selected_node && !selected_link) return;
switch (d3.event.keyCode) {
case 8: // backspace
case 46: // delete
if (selected_node) {
nodes.splice(nodes.indexOf(selected_node), 1);
spliceLinksForNode(selected_node);
} else if (selected_link) {
links.splice(links.indexOf(selected_link), 1);
}
selected_link = null;
selected_node = null;
restart();
break;
case 66: // B
if (selected_link) {
// set link direction to both left and right
selected_link.left = true;
selected_link.right = true;
}
restart();
break;
case 76: // L
if (selected_link) {
// set link direction to left only
selected_link.left = true;
selected_link.right = false;
}
restart();
break;
case 82: // R
if (selected_node) {
// toggle node reflexivity
selected_node.reflexive = !selected_node.reflexive;
} else if (selected_link) {
// set link direction to right only
selected_link.left = false;
selected_link.right = true;
}
restart();
break;
}
}
function keyup() {
lastKeyDown = -1;
// ctrl
if (d3.event.keyCode === 17) {
rect
.on('mousedown.drag', null)
.on('touchstart.drag', null);
svg.classed('ctrl', false);
}
}
// app starts here
svg.on('mousedown', mousedown)
.on('mousemove', mousemove)
.on('mouseup', mouseup);
d3.select(window)
.on('keydown', keydown)
.on('keyup', keyup);
restart();
</script>
</body>
</html>
这篇关于如何将标记头放在链接的中间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!