我使用D3.js树 for each node 呈现一个量规.只有根 node 呈现仪表指针(路径对象).我研究了几个来源,试图了解这个问题,但我无法确定原因.
在本例中,我的目标是显示指向所有 node 的指针,使用Json"pointerValue"属性定位指针.感谢大家的关注.
这是我的代码
<html>
<style>
g.arc {
fill: steelblue;
}
g.pointer {
fill: #e85116;
stroke: #b64011;
}
g.label text {
text-anchor: middle;
font-size: 10px;
font-weight: bold;
fill: #666;
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 14px sans-serif;
}
.node text:hover {
font-weight: bold;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
function deg2rad(deg) {
return deg * Math.PI / 180;
}
function newAngle(d) {
var ratio = scale(d);
var newAngle = config.minAngle + (ratio * range);
return newAngle;
}
function centerTranslation(r) {
return 'translate('+ r +','+ r +')';
}
var config = {
size : 100,
clipWidth : 100,
clipHeight : 60,
ringInset : 20,
ringWidth : 20,
pointerWidth : 10,
pointerTailLength : 5,
pointerHeadLengthPercent : 0.8,
minValue : 0,
maxValue : 100,
minAngle : -90,
maxAngle : 90,
transitionMs : 3000,
majorTicks : 5,
labelFormat : d3.format('d'),
labelInset : 10,
arcColorFn : d3.interpolateHsl(d3.rgb('#e8e2ca'), d3.rgb('#3e6c0a'))
};
range = config.maxAngle - config.minAngle;
r = config.size / 2;
pointerHeadLength = Math.round(r * config.pointerHeadLengthPercent);
scale = d3.scaleLinear()
.range([0,1])
.domain([config.minValue, config.maxValue]);
ticks = scale.ticks(config.majorTicks);
tickData = d3.range(config.majorTicks).map(function() {return 1/config.majorTicks;});
var arc = d3.arc()
.innerRadius(r - config.ringWidth - config.ringInset)
.outerRadius(r - config.ringInset)
.startAngle(function(d, i) {
var ratio = d * i;
return deg2rad(config.minAngle + (ratio * range));
})
.endAngle(function(d, i) {
var ratio = d * (i+1);
return deg2rad(config.minAngle + (ratio * range));
});
var lineData = [ [config.pointerWidth / 2, 0],
[0, -pointerHeadLength],
[-(config.pointerWidth / 2), 0],
[0, config.pointerTailLength],
[config.pointerWidth / 2, 0] ];
let treeData =
{
"id": "0",
"name": "Root",
"pointerValue": "10",
"children": [
{
"id": "1",
"name": "Node x",
"pointerValue": "55",
"children": [
{
"id": "2",
"name": "Node y",
"pointerValue": "75"
},
{
"id": "3",
"name": "Node z",
"pointerValue": "95"
}
]
},
{
"id": "4",
"name": "Node w",
"pointerValue": "35"
}
]
};
// Set the dimensions and margins of the diagram
var margin = {top: 20, right: 90, bottom: 30, left: 90},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate("
+ margin.left + "," + margin.top + ")");
var i = 0,
duration = 750,
root;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
// Assigns parent, children, height, depth
root = d3.hierarchy(treeData, function(d) { return d.children; });
root.x0 = height / 2;
root.y0 = 0;
// Collapse after the second level
root.children.forEach(collapse);
update(root);
// Collapse the node and all it's children
function collapse(d) {
if(d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function update(source) {
// Assigns the x and y position for the nodes
var treeData = treemap(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function(d){ d.y = d.depth * 400});
// ****************** Nodes section ***************************
// Update the nodes...
var node = svg.selectAll('g.node')
.data(nodes, function(d) {
//alert(d.data.name);
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append('g')
.attr('class', 'node')
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
}).on('click', click);
nodeEnter.append('text')
.attr("dy", ".35em")
.attr("x", d => d.children || d._children ? 50 : -55)
.attr("y", -5)
//.attr("id", function(d) {
//alert(d.data.id);
// return "node-" + d.data.id;
//})
.attr("text-anchor", d => d.children || d._children ? "end" : "start")
.text(d => d.data.name);
//#########################################################################
var vis = nodeEnter.append('svg')
//.attr("id", function(d) {
// return "gauge-" + d.data.id;
//})
.attr('class', 'gauge')
.attr('width', config.clipWidth)
.attr('height', config.clipHeight)
.attr("y", -70)
.attr("x", -50);
var arcs = vis.append('g')
//.attr("id", function(d) {
// return "arc-" + d.data.id;
//})
.attr('class', 'arc')
.attr('transform', centerTranslation(r));
arcs.selectAll('path')
.data(tickData)
.enter().append('path')
.attr('fill', function(d, i) {
return config.arcColorFn(d * i);
})
.attr('d', arc);
var lg = vis.append('g')
//.attr("id", function(d) {
// return "label-" + d.data.id;
//})
.attr('class', 'label')
.attr('transform', centerTranslation(r));
lg.selectAll('text')
.data(ticks)
.enter().append('text')
.attr('transform', function(d) {
var ratio = scale(d);
var newAngle = config.minAngle + (ratio * range);
return 'rotate(' + newAngle +') translate(0,' +(config.labelInset - r) +')';
})
.text(config.labelFormat);
var pg = vis.append('g')
.data([lineData])
.attr('class', 'pointer')
.attr('transform', centerTranslation(r));
//########################################
//THIS PART JUST WORKS FOR ROOT NODE
//########################################
var ratio = scale(87);
var newAngle = config.minAngle + (ratio * range);
pg.append('path')
//d attribute that defines its shape and position.
.attr("d", d3.line().curve(d3.curveLinear) )
.attr("stroke", "black")
.attr('transform', 'rotate(' +config.minAngle +')')
.transition()
.duration(config.transitionMs)
.ease(d3.easeElastic)
.attr('transform', 'rotate(' + newAngle +')');
//#########################################################################
// UPDATE
var nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 10)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');
// Remove any exiting nodes
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6);
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6);
// ****************** links section ***************************
// Update the links...
var link = svg.selectAll('path.link')
.data(links, function(d) { return d.id; });
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d){ return diagonal(d, d.parent) });
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
}
</script>
</body>
</html>