d3 drag blurring/jittery handle - javascript

This is on d3 v4.
I'm trying to create an expandable rectangle area, with bounds (sort of a constrained d3-brush). I add a handle which shows up on mouseover.
var rectHeight = 80, rectWidth = 100, maxWidth = 200;
var svg = d3.select("svg");
var brect = svg.append("g")
.attr("id", "brect");
brect.append("rect")
.attr("id", "dataRect")
.attr("width", rectWidth)
.attr("height", rectHeight)
.attr("fill", "green");
var handleResizeGroup = brect.append("g")
.attr("id", "handleResizeGroup")
.attr("transform", `translate(${rectWidth})`)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded));
function dragStarted() {
d3.select(this.previousSibling).classed("active", true);
}
function dragEnded() {
d3.select(this.previousSibling).classed("active", false);
}
function dragged(d) {
var h = d3.select(this);
var r = d3.select(this.previousSibling);
var currWidth = r.attr("width");
var t = (d3.event.x >= 0 && d3.event.x <= maxWidth) ? d3.event.x : currWidth;
r.attr("width", t);
h.attr("transform", `translate(${t})`)
}
handleResizeGroup.append("path")
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0)
.attr("stroke", "grey")
.attr("cursor", "ew-resize")
.attr("d", resizePath);
handleResizeGroup.append("rect")
.attr("id", "resizeRect")
.attr("width", "8")
.attr("fill-opacity", 0)
.attr("cursor", "ew-resize")
.attr("height", rectHeight)
//.attr("pointer-events", "all")
.on("mouseover", function(){
d3.select(this.previousSibling)
.attr("stroke-opacity", "100%");
})
.on("mouseout", function() {
d3.select(this.previousSibling)
.attr("stroke-opacity", "0");
});
function resizePath(d) {
var e = 1,
x = e ? 1 : -1,
y = rectHeight / 3;
return "M" + (.5 * x) + "," + y
+ "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6)
+ "V" + (2 * y - 6)
+ "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y)
+ "Z"
+ "M" + (2.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8)
+ "M" + (4.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8);
}
rect.active {
stroke-width: 1;
stroke: rgb(0,0,0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width=1200 height=500></svg>
I'm noticing 2 issues
When I drag the handle, I see a jitter in the handle itself (presumably because handle is shown only on mouseover?)
If I drag the mouse too fast - say to the left, the rectangle does not catch-up
Can someone help to understand what's going on, and how to fix these?
Thank you!

For your first issue, add .attr('pointer-events', 'none') to the greenrect`. the handle is jittering because your cursor moves slightly faster than the handle does- so you're constantly mousing-out of and then mousing-in to the handle as you move and it catches up.
I don't really see what you're doing with resizePath. Is that adding the grey stroke around the rect? Why not just add/remove a stroke? Your second issue may be due to constantly resizing that path.

Related

D3.js static force layout not working with path?

Im trying change this example https://bl.ocks.org/mbostock/1667139 to use path instead of line, but its not working.
Im try use own tick function like this:
function tick() {
link.attr("d", function(d) {
var x1 = d.source.x,
y1 = d.source.y,
x2 = d.target.x,
y2 = d.target.y,
dx = x2 - x1,
dy = y2 - y1,
dr = Math.sqrt(dx * dx + dy * dy),
// z uzla do ineho uzla
drx = dr,
dry = dr,
xRotation = 0,
largeArc = 0,
sweep = 1;
//do sameho seba
if ( x1 === x2 && y1 === y2 ) {
xRotation = -45;
largeArc = 1;
drx = 30;
dry = 30;
x2 = x2 + 1;
y2 = y2 + 1;
}
return "M" + x1 + "," + y1 + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + x2 + "," + y2;
});
}
I dont know, if i missing something or static force layout just cant use path.
Force layout with path working normaly
From the docs (bolding mine):
simulation.tick()
Increments the current alpha by (alphaTarget - alpha) × alphaDecay;
then invokes each registered force, passing the new alpha; then
decrements each node’s velocity by velocity × velocityDecay; lastly
increments each node’s position by velocity.
This method does not dispatch events; events are only dispatched by
the internal timer when the simulation is started automatically upon
creation or by calling simulation.restart. The natural number of ticks
when the simulation is started is ⌈log(alphaMin) / log(1 -
alphaDecay)⌉; by default, this is 300.
This method can be used in conjunction with simulation.stop to compute
a static force layout. For large graphs, static layouts should be
computed in a web worker to avoid freezing the user interface.
Since it doesn't dispatch events your tick function is never called or used. Instead, just replace the line and set your path up once:
<!DOCTYPE html>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var n = 100,
nodes = d3.range(n).map(function(i) {
return {
index: i
};
}),
links = d3.range(n).map(function(i) {
return {
source: i,
target: (i + 3) % n
};
});
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-80))
.force("link", d3.forceLink(links).distance(20).strength(1).iterations(10))
.force("x", d3.forceX())
.force("y", d3.forceY())
.stop();
var loading = svg.append("text")
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.text("Simulating. One moment please…");
// Use a timeout to allow the rest of the page to load first.
d3.timeout(function() {
loading.remove();
// See https://github.com/d3/d3-force/blob/master/README.md#simulation_tick
for (var i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
simulation.tick();
}
g.append("g")
.attr("stroke", "#000")
.attr("stroke-width", 1.5)
.selectAll("line")
.data(links)
.enter().append("path")
.attr("d", function(d) {
var x1 = d.source.x,
y1 = d.source.y,
x2 = d.target.x,
y2 = d.target.y,
dx = x2 - x1,
dy = y2 - y1,
dr = Math.sqrt(dx * dx + dy * dy),
// z uzla do ineho uzla
drx = dr,
dry = dr,
xRotation = 0,
largeArc = 0,
sweep = 1;
//do sameho seba
if (x1 === x2 && y1 === y2) {
xRotation = -45;
largeArc = 1;
drx = 30;
dry = 30;
x2 = x2 + 1;
y2 = y2 + 1;
}
return "M" + x1 + "," + y1 + "A" + drx + "," + dry + " " + xRotation + "," + largeArc + "," + sweep + " " + x2 + "," + y2;
});
g.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 4.5);
});
</script>
Response to comments:
To append a circle and text as a "node", I would create a g, position that and then put the circle and text in it:
var g = node
.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("transform", function(d){
return "translate(" + d.x + "," + d.y + ")";
});
g.append("circle")
.attr("class", "node")
.attr("stroke", "#fff")
.attr("r", 28);
g.append("text")
.text("test");

d3 (v4): giving elements the appearance of momentum?

Suppose you want to have an html page where the entire background is an svg, such that there is some animation going on constantly. In this silly example, I have made a smiley face which randomly moves about. While some people might find brownian motion appealing to the eye, it would be nicer if the svg element could move with the appearance of momentum (both direction and rotation).
One might quickly realize that having an object move along a path would solve this issue and it would... for the first pass of the object. However if the element were to be bounded by the screen, then how could one get the transition to adjust for the deflection?
In short the question is as follows:
Using d3.js v4 how can I make an svg element (such as #Mr_Smiley in the demo below) appear to float* across the html page?
*let float mean smoothly move with constant velocity along a vector or arc within the svg space with recoil upon hitting borders and correct deflection
var mr_s = d3.select("svg").append("g").attr("id", "Mr_Smiley")
mr_s.append("circle").attr("cx", 30).attr("cy", 30).attr("r", 30).style("fill","yellow").style("stroke", "black")
mr_s.append("circle").attr("cx", 20).attr("cy", 20).attr("r", 5).style("fill","black")
mr_s.append("circle").attr("cx", 40).attr("cy", 20).attr("r", 5).style("fill","black")
mr_s.append("path").attr("d", "M20 40 A 10 10 0 0 0 40 40").style("fill","black")
mr_s.datum({"x": 30, "y": 30, "r": 1})
mr_s.attr("transform", function(d) {"translate(" + d.x + ", " + d.y + ") rotate(" + d.r + ")"})
dur = 100
step = 10
// Lets have Mr. S go for a trip
d3.select("#Mr_Smiley")
.transition()
.duration(dur)
.on("start", function repeat() {
d3.active(this)
.attr("transform",
function(d)
{
// update y
if (Math.random() >= .5) {
d.y += step
} else {
d.y -= step
// basic bounds
if (d.y < 0) {
d.y = 0
}
}
// update x
if (Math.random() >= .5) {
d.x += step
} else {
d.x -= step
if (d.x < 0) {
d.x = 0
}
}
// update r
if (Math.random() >= .5) {
d.r += step
} else {
d.r -= step
}
return "translate(" + d.x + ", " + d.y + ") rotate(" + d.r + ")"
})
.transition()
.attr("transform",
function(d)
{
// update y
if (Math.random() >= .5) {
d.y += step
} else {
d.y -= step
}
// update x
if (Math.random() >= .5) {
d.x += step
} else {
d.x -= step
}
// update r
if (Math.random() >= .5) {
d.r += step
} else {
d.r -= step
}
return "translate(" + d.x + ", " + d.y + ") rotate(" + d.r + ")"
})
.transition()
.on("start", repeat)
})
mr_s.on("mouseover", mouseover)
mr_s.on("mouseout", mouseout)
function mouseover(d, i) {
var svg = d3.select("svg")
svg.append("text").attr("id", "mouseover_text").text("god help me, this is so unsmooth").attr("x", d.x).attr("y", d.y)
}
function mouseout(d, i) {
d3.select("#mouseover_text").remove()
}
html, body {
width: 100%;
height: 100%;
}
svg {
position: absolute;
width: 100%;
height: 100%;
background-color: light-blue;
border: 1px black solid;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
<h1>
Mr. Smiley goes on a trip
</h1>
Here's the simplest Mr S. bounce around a room I can code using d3 conventions:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var w = 250,
h = 250,
r = 30;
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
.style('border', '1px solid steelblue');
var ix = Math.random() * ((Math.random() > 0.5) ? 5 : -5),
iy = Math.random() * ((Math.random() > 0.5) ? 5 : -5),
x = w / 2,
y = h / 2;
var mr_s = svg.append("g")
.attr("id", "Mr_Smiley")
mr_s.append("circle")
.attr("r", r)
.style("fill", "yellow")
.style("stroke", "black");
mr_s.append("circle")
.attr("cx", -10)
.attr("cy", -10)
.attr("r", 5)
.style("fill", "black");
mr_s.append("circle")
.attr("cx", 10)
.attr("cy", -10)
.attr("r", 5)
.style("fill", "black");
mr_s.append("path")
.attr("d", "M-10 10 A 10 10 0 0 0 10 10")
.style("fill", "black");
mr_s.attr('transform', 'translate(' + x + ',' + y + ')');
d3.interval(tick, 20);
function tick() {
x += ix;
y += iy;
if (x > (w - r) || x < r) {
ix = -ix;
}
if (y > (h - r) || y < r) {
iy = -iy;
}
mr_s.attr('transform', 'translate(' + x + ',' + y + ')');
}
</script>
</body>
</html>

Edge Labels Not Shown

I am trying to implement labeled edges on a force directed graph.
The example I use can be found here.
The relevant bits of code in the example are given here.
My code is the following:
<style>
.node {
stroke: #fff;
stroke-width: 0.5px;
}
.node text {
pointer-events: none;
font: 15px helvetica;
}
.link {
fill: none;
stroke: #bbb;
stroke-width: 3.0px;
opacity: 0.5;
}
.highlight {
stroke: #259359;
}
</style>
<body>
<script src= "//d3js.org/d3.v3.min.js" > </script>
<script>
var width = 700,
height = 550;
var color = d3.scale.category20();
var force = d3.layout.force()
.linkStrength(1)
.distance(0.01)
.gravity(0.2)
.charge(-500)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter().append("marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 16)
.attr("refY", 0)
.attr("markerWidth", 3)
.attr("markerHeight", 3)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.style("stroke", "#bbb");
d3.json("fg.json", function(error, graph) {
if (error) throw error;
var nodes = graph.nodes.slice(),
links = [],
bilinks = [];
graph.links.forEach(function(link) {
var s = nodes[link.source],
t = nodes[link.target],
i = {}; // intermediate node
nodes.push(i);
links.push({
source: s,
target: i
}, {
source: i,
target: t
});
bilinks.push([s, i, t]);
});
force
.nodes(nodes)
.links(links)
.size([width, height])
.start();
var link = svg.selectAll(".link")
.data(bilinks)
.enter().append("path")
.attr("class", "link")
.style("marker-end", "url(#end)")
.on("mouseover", function() {
d3.select(d3.event.target).classed("highlight", true);
})
.on("mouseout", function() {
d3.select(d3.event.target).classed("highlight", false);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag)
node.append("circle")
.attr("r", 8)
.style("fill", function(d) {
return color(d.group);
})
node.append("text")
.attr("dx", 15)
.attr("dy", ".40em")
.text(function(d) {
return d.name
})
.style("stroke", "gray");
//
var padding = 30, // separation between circles
radius = 1;
function collide(alpha) {
var quadtree = d3.geom.quadtree(graph.nodes);
return function(d) {
var rb = 2 * radius + padding,
nx1 = d.x - rb,
nx2 = d.x + rb,
ny1 = d.y - rb,
ny2 = d.y + rb;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y);
if (l < rb) {
l = (l - rb) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
var edgepaths = svg.selectAll(".edgepath")
.data(graph.links)
.enter()
.append('path')
.attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y},
'class':'edgepath',
'fill-opacity':0,
'stroke-opacity':0,
'fill':'blue',
'stroke':'red',
'id':function(d,i) {return 'edgepath'+i}})
.style("pointer-events", "none");
var edgelabels = svg.selectAll(".edgelabel")
.data(graph.links)
.enter()
.append('text')
.style("pointer-events", "none")
.attr({'class':'edgelabel',
'id':function(d,i){return 'edgelabel'+i},
'dx':80,
'dy':0,
'font-size':10,
'fill':'#aaa'});
edgelabels.append('textPath')
.attr('xlink:href',function(d,i) {return '#edgepath'+i})
.style("pointer-events", "none")
.text(function(d,i){return 'label '+i});
force.on("tick", function() {
link.attr("d", function(d) {
return "M" + d[0].x + "," + d[0].y + "S" + d[1].x + "," + d[1].y + " " + d[2].x + "," + d[2].y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
edgepaths.attr('d', function(d) { var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
return path});
edgelabels.attr('transform',function(d,i){
if (d.target.x<d.source.x){
bbox = this.getBBox();
rx = bbox.x+bbox.width/2;
ry = bbox.y+bbox.height/2;
return 'rotate(180 '+rx+' '+ry+')';
}
else {
return 'rotate(0)';
}
});
node.each(collide(0.5));
});
});
</script>
The data is given below:
{
"nodes":[
{"name":"alkene","group":1},
{"name":"alkane","group":1},
{"name":"halogenoalkane","group":2},
{"name":"dihalogenoalkane","group":2},
{"name":"amine","group":3},
{"name":"alcohol","group":4},
{"name":"ketone","group":5},
{"name":"aldehyde","group":6},
{"name":"hydroxynitrile","group":7},
{"name":"ester","group":8},
{"name":"carboxylic acid","group":9},
{"name":"acyl chloride","group":9},
{"name":"amide","group":10},
{"name":"nitrile","group":11}
],
"links":[
{"source":0,"target":2,"value":2},
{"source":0,"target":1,"value":1},
{"source":2,"target":0,"value":8},
{"source":0,"target":3,"value":10},
{"source":2,"target":4,"value":10},
{"source":5,"target":2,"value":1},
{"source":2,"target":5,"value":1},
{"source":6,"target":5,"value":1},
{"source":5,"target":6,"value":1},
{"source":7,"target":5,"value":1},
{"source":5,"target":7,"value":1},
{"source":7,"target":8,"value":2},
{"source":7,"target":10,"value":1},
{"source":10,"target":7,"value":1},
{"source":5,"target":9,"value":3},
{"source":10,"target":9,"value":3},
{"source":13,"target":10,"value":5},
{"source":10,"target":11,"value":1},
{"source":11,"target":10,"value":1},
{"source":11,"target":12,"value":1}
]
}
Unfortunately, the labels on the graph are not visible.
The final objective is to show the corresponding value "value" on each edge.
Could you please tell me what I am doing wrong?
Thank you for your time.
UPDATE
The labels were sucessfully added to the edges by subbing
"M" + d[0].x + "," + d[0].y + "S" + d[1].x + "," + d[1].y + " " + d[2].x + "," + d[2].y
for
'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y
However, the question remains: how can the "value" datum be added instead of the labels "label i"? Moreover, how can their appearance on mouseover be implemented?
UPDATE II
The "value" datum was made to be shown by defining .data(graph.links) for textPath of edgelabels and then returning a d.value. Could you please tell me how the mouseover can be implemented? It would be nice if the "value" datum of each edge would be seen only on hover. Thank you!
From this example : http://jsfiddle.net/7HZcR/3/
I have created the same view but with your data here : http://jsfiddle.net/thatOneGuy/7HZcR/515/
So you need to implement the arrows.
What you had previously, you couldn't log the data on mouseover as the data you brought through didn't contain it.
So in this one I have brought the data through like so :
var links = graph.links;
Set it to your data, but since this only has indexes as source and target you need to attach nodes to the source and target so it targets correctly :
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
Now the data brought back would be along the lines of :
{
source : someNode,
target : someNode,
value : linkValue
}
So I have created a text output as to work out where to put the value on the link would be difficult as you have to work out the curve etc :
var textOutput = svg.append('text').attr('class', 'textOutput')
.attr("transform","translate(50, 100)");
So on mouseover set the textOutput :
.on('mouseover', function(d){
console.log(d);
d3.select(this).style('stroke', 'red').style('stroke-width', '5')
textOutput.text('LINK VALUE : ' + d.value);
})
.on('mouseout', function(d){
d3.select(this).style('stroke', '#666') .style('stroke-width', '1')
});
Hope that helps :)

d3.js - how to arrange the `squre` box around the `circle` properly

I am trying to arrange the squares around the circle but i am unable to get the correct output.
Can any one help me?
// largely based on http://bl.ocks.org/4063550
// some made-up data
var data = [2,2,2,2,2,2];
// tree-ify our fake data
var dataTree = {
children: data.map(function(d) { return { size: d }; })
};
// basic settings
var w = 300,
h = 300,
maxRadius = 75;
// size scale for data
var radiusScale = d3.scale.sqrt().domain([0, d3.max(data)]).range([0, maxRadius]);
// determine the appropriate radius for the circle
var roughCircumference = d3.sum(data.map(radiusScale)) * 2,
radius = roughCircumference / (Math.PI * 2);
// make a radial tree layout
var tree = d3.layout.tree()
.size([360, radius])
.separation(function(a, b) {
return radiusScale(a.size) + radiusScale(b.size);
});
// make the svg
var svg = d3.select("body").append("svg")
.attr("width", w )
.attr("height", h )
.append("g")
.attr("transform", "translate(" + (w / 2 ) + "," + (h /2) + ")");
var c = svg.append('circle').attr({r:75})
// apply the layout to the data
var nodes = tree.nodes(dataTree);
// create dom elements for the node
var node = svg.selectAll(".node")
.data(nodes.slice(1)) // cut out the root node, we don't need it
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
console.log(d.x);
return "rotate(" + (d.x - 90) + ") translate(" + d.y + ")";
})
node.append("rect")
.attr({
width: 25,
height:25,
fill : 'red',
"transform":function(d) {
return "rotate(" + (-1 * d.x + 90) + ") translate(" +0+ ")";
}
});
node.append("text")
.attr({"transform":function(d) {
return "rotate(" + (-1 * d.x + 90) + ")";
},
"text-anchor": "middle"
})
.text("testing a word");
svg {
border:1px solid gray;
}
circle {
fill: steelblue;
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I am looking the output like this:
A sample code to work with.
I have assumed 8 nodes to be plotted so that the circle can be divided into 8 segments. Each square to placed at the distance of Pi/4 radians. You can compute the x,y as xSin , y Cos. Then you will need to transform the rectangle to centre at x,y rather than the top left corner.
// largely based on http://bl.ocks.org/4063550
// some made-up data
var data = [2,2,2,2,2,2,2,2];
// tree-ify our fake data
var dataTree = {
children: data.map(function(d) { return { size: d }; })
};
// basic settings
var w = 300,
h = 300,
maxRadius = 75;
// size scale for data
var radiusScale = d3.scale.sqrt().domain([0, d3.max(data)]).range([0, maxRadius]);
// determine the appropriate radius for the circle
var roughCircumference = d3.sum(data.map(radiusScale)) * 2,
radius = roughCircumference / (Math.PI * 2);
// make a radial tree layout
var tree = d3.layout.tree()
.size([360, radius])
.separation(function(a, b) {
return radiusScale(a.size) + radiusScale(b.size);
});
// make the svg
var svg = d3.select("body").append("svg")
.attr("width", w )
.attr("height", h )
.append("g")
.attr("transform", "translate(" + (w / 2 ) + "," + (h /2) + ")");
var c = svg.append('circle').attr({r:75})
var r = 75;
// apply the layout to the data
var nodes = tree.nodes(dataTree);
// create dom elements for the node
var node = svg.selectAll(".node")
.data(nodes.slice(1)) // cut out the root node, we don't need it
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d,i) {
return "translate(" + (r * Math.sin(Math.PI * i * 0.25)) + "," + (r * Math.cos(Math.PI * i * 0.25)) + ")";
})
node.append("rect")
.attr({
width: 25,
height:25,
fill : 'red',
"transform":function(d) {
return "translate(" +(-12.5)+ ","+ (-12.5) + ")";
}
});
node.append("text")
.attr({"transform":function(d) {
return "rotate(" + (-1 * d.x + 90) + ")";
},
"text-anchor": "middle"
})
.text("testing a word");
svg {
border:1px solid gray;
}
circle {
fill: steelblue;
stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Here's a fiddle.
Okay, so this was basically some pixel level trial-and-error translate manipulation. This is for node.
.attr("transform", function(d) {
console.log(d.x);
return "rotate(" + (d.x - 90) + ") translate(" + (d.y - 65 ) + ")";
})
and this for rect:
.attr({
width: 25,
height:25,
fill : 'red',
"transform":function(d) {
return "rotate(" + -(d.x - 90) + ") translate(" +(-10)+ ","+ (-10) + ")";
}
});

Have to save text height in d using d3 library

I am trying to save text height value in d (so I can draw around appropriate size rectangle), but it's not defined in tick function or any where else.
d.height
should hold values. (but it doesn't)
So I have tick function (part of it):
function tick() {
z.attr('d', function(d) {
alert (d.height); //not defined
var sourceX = d.source.x + textWidth/2 + 10,
sourceY = d.source.y + d.height/2 + 10,
targetX = d.target.x + textWidth/2 + 10,
targetY = d.target.y + d.height/2 + 10;
return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
});
shape.attr('transform', function(d) {
middle_rect.attr("height", d.height/2 + 20);
side_rect.attr("height", d.height/2 + 20);
return 'translate(' + d.x + ',' + d.y + ')';
});
And here how I'm trying to save values (bottom of code)
shape = shape.data(nodes);
// add new nodes
var g = shape.enter().append('svg:g').attr('class', function(d) { return d.type +' node'; });
middle_rect = svg.selectAll(".middle")
.append("svg:rect")
.attr("rx", "10")
.attr("ry", "10")
.style("stroke", "rgb(47,136,214)")
.style("fill", "url(#middle_gradient)");
side_rect = svg.selectAll(".side")
.append("svg:rect")
.attr("rx", "10")
.attr("ry", "10")
.style("stroke", "rgb(47,136,214)")
.style("fill", "url(#side_gradient)");
txt = g.append('svg:text')
.attr('class',function (d){return 'bigest ' + d.id ;} )
.attr('y', ".5em")
.attr("dy", "1em")
.style("fill", "black")
.each (function (d) {
d.height = this.getBoundingClientRect().height;
// alert (d.height); //here I have this value
});
Can I make it global? Or have to save these values?
It appears that you are trying to attach a height field to the Node itself, which is considered a bad practice in general.
Why not create a data structure to store all these values, or possibly even add the height to your dataset.
Another approach could be creating a data-height attribute on the text node.
http://jsfiddle.net/heavyhorse/893jT/
svg.selectAll('text.data-label').each(function(d, i){
var height = this.getBoundingClientRect().height;
var width = this.getBoundingClientRect().width;
console.log(i+': ('+width+','+height+')');
// create custom data-attr:
this.setAttribute('data-width', width);
this.setAttribute('data-height', height);
console.log(this);
// attach to dataset:
dataset[i].width = width;
dataset[i].height = height;
});

Categories