D3.js Arrow-like path around text - d3.js

I want to reproduce this beautiful arrow-like tooltip and since the source code is not accessible i am asking myself:
how to make a "background" path for the tooltip body which fits the overlaying text block.
Do i have to create the text element and then calculate the width via getBoundingClientRect() and insert the width it into a custom path ?
Edit: just found the solution in a non minified js of bostock.
var tooltipRect = tooltipContent.node().getBoundingClientRect(),
tooltipWidth = tooltipRect.width,
tooltipHeight = tooltipRect.height;
tooltipPath
.attr("width", tooltipWidth)
.attr("height", tooltipHeight + 6)
.select("path")
.attr("d", "M0,0"
+ "H" + tooltipWidth
+ "v" + tooltipHeight
+ "H" + (tooltipWidth / 2 + 6)
+ "l-6,6"
+ "l-6,-6"
+ "H0"
+ "z");
tooltipOverlay
.style("left", (d.x + margin.left - tooltipWidth / 2) + "px")
.style("top", (d.y + margin.top - tooltipHeight - 6) + "px");

Related

Drawing link with d3 and d3-flextree plugin

I'm (still) trying to make an orgchart with D3 and d3-flextree plugin. I struggle drawing the links between the nodes. The "equation" I use consider the middle of the node (as I understand it) whereas I'd like to draw from the end of a node.
I think my mistake is in my drawing-link function
function diagonal(s, d) {
path = `M ${s.x} ${s.y}
L ${s.x} ${(s.y + d.y) * 0.5},
${d.x} ${(s.y + d.y) * 0.5 },
${d.x} ${d.y}`
return path
}
It's hard to explain so I made a JSFiddle: https://jsfiddle.net/ymv5sr9k/11/
In this exemple all links are the way I want thanks to right padding, but as soon as the nodeSize change (see the big node) it's all broken. I guess I need a more general drawing-link function but I can't figure it out
Thanks for reading,
Zoom
Problem solved! As i said in the comments, I needed to move the horizontal line. I added nodeSize in my two y "control points". And nodeSize is actually source.y - destination.y - padding. This is my final equation:
function diagonal(s, d) {
var nodeSize = s.y - d.y - 30
return "M" + s.x + "," + s.y
+ "L" + s.x + "," + (d.y + s.y + nodeSize) / 2
+ " " + d.x + "," + (d.y + s.y + nodeSize) / 2
+ " " + d.x + "," + d.y;
};
Thank you for your time!

How to dynamically set dates from data in d3 calendar view?

In the d3 calendar view example, I am trying to modify it to automatically determine the range of years to display from the data given it.
Is it possible to have d3 automatically figure out what arguments to pass to d3.range() to automatically determine the start and end year, rather than have hard-coded literals?
In the .data() line below, I've tried using d3.min() and d3.max() as arguments to d3.range(), but that's not correct. I've tried sorting the nested data beforehand to obtain the first and last entry in the array, but that has not worked. Suggestions?
return d3.select( "body" )
.selectAll( "svg" )
.data(d3.range(1990, 2010))
.enter()
.append( "svg" )
.attr( "width", width )
.attr( "height", height )
.attr( "class", "year" )
.append( "g" )
.attr( "transform", "translate(" + getOffsetX() + "," + getOffsetY() + ")" );
Put everything that is done before the d3.csv("dji.csv", inside the callback and after the d3.nest(), modify the following line
var range = d3.extent(csv, d => Number(d.Date.substring(0,4)));
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(range[0], range[1]))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");

Is it possible to create an SVG rect element with top left and top right rounded corners for use in D3? [duplicate]

This question already has answers here:
svg / d3.js rounded corners on one side of a rectangle
(5 answers)
Closed 6 years ago.
Is there a simple way to place rounded corners just on the top of the Bar(s) in a D3 Vertical Bar Chart? I've been playing around with .attr("rx", 3) and that seems to affect all four corners of a Bar.
You cannot specify which corners you want to make round in SVG: rx will affect all 4 corners.
The only solution is using a path for simulating a rectangle. This function returns a path with top corners round:
function rectangle(x, y, width, height, radius){
return "M" + (x + radius) + "," + y + "h" + (width - 2*radius)
+ "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius + "v" +
(height - 2*radius) + "v" + radius + "h" + -radius + "h" +
(2*radius - width) + "h" + -radius + "v" + -radius + "v" +
(2*radius - height) + "a" + radius + "," + radius + " 0 0 1 "
+ radius + "," + -radius + "z";
};
Here is a demo snippet showing a "bar chart" with those paths, with a radius (the rx equivalent here) of 5px:
function rectangle(x, y, width, height, radius){
return "M" + (x + radius) + "," + y + "h" + (width - 2*radius) + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius + "v" + (height - 2*radius) + "v" + radius + "h" + -radius + "h" + (2*radius - width) + "h" + -radius + "v" + -radius + "v" + (2*radius - height) + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + -radius + "z";
};
var data = [40, 50, 30, 40, 90, 54, 20, 35, 60, 42];
var svg = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 120);
var rects = svg.selectAll(".paths").data(data).enter().append("path");
rects.attr("d", function(d,i){ return rectangle(10+40*i,100-d,20,d,5)});
var texts = svg.selectAll(".text").data("ABCDEFGHIJ".split("")).enter().append("text").attr("y",114).attr("x", function(d,i){return 16+40*i}).text(function(d){return d});
path {
fill:teal;
}
text {
fill:darkslategray;
font-size: 12px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
PS: I didn't write that function, it was based on these answers by M. Bostock and R. Longson.

Removing single elements of svg in d3.js

I have the following code in d3.js
var svg = d3.select(".Canvas").append("svg").attr(
"width", width + margin.left + margin.right).attr("height",
height + margin.top + margin.bottom).append("g").attr(
"transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.append("g").attr("class", "x axis").attr("transform",
"translate(0," + height + ")").call(xAxis);
svg.append("g").attr("class", "y axis").call(yAxisMajor).append("text")
.attr("transform", "rotate(-90)").attr("y", 6).attr("dy",
".71em").style("text-anchor", "end");
I tried the following to remove only the y-axis, not the whole svg:
d3.select(".Canvas").selectAll("svg").selectAll("g").select(".y axis").remove();
Why is the code not working?
Here is a little demo with a couple of simple ways to do it (see my comments in the fiddle).
d3.select("svg > g > #the_one_circle")
.transition().duration(1000)
.attr("r",1)
.remove();
Note how the svg and g elements are still there after removing the circle.
Because when you set class attribute as y axis , you actually set two classes. So in order to select and match both class names you need to call select with .y.axis. So that the result should be:
d3.select(".canvas").selectAll("svg").selectAll("g").select(".y.axis").remove();
at least worked for me, demo.

d3 drawing arrows tips

In this example :
http://jsfiddle.net/maxl/mNmYH/2/
If I enlarge the circles, ex:
var radius = 30; // (is 6 in the jsFiddle)
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", radius)
What is the best way to properly adjust the drawing of the arrow
so that it points to the radius of the circle ?
Thanks
You asked for the "best way to properly adjust the drawing of the arrow ".
I cannot claim the following approach is the "best" way, and I look forward to other answers, but here is one method to tackle this issue.
http://jsfiddle.net/Y9Qq3/2/
Relevant updates are noted below.
...
var w = 960,
h = 500
markerWidth = 6,
markerHeight = 6,
cRadius = 30, // play with the cRadius value
refX = cRadius + (markerWidth * 2),
refY = -Math.sqrt(cRadius),
drSub = cRadius + refY;
...
svg.append("svg:defs").selectAll("marker")
.data(["suit", "licensing", "resolved"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", refX)
.attr("refY", refY)
.attr("markerWidth", markerWidth)
.attr("markerHeight", markerHeight)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
...
function tick() {
path.attr("d", function (d) {
var dx = d.target.x - d.source.x,
dy = (d.target.y - d.source.y),
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + (dr - drSub) + "," + (dr - drSub) + " 0 0,1 " + d.target.x + "," + d.target.y;
});
...

Resources