How to translate all text in svg container that are already rotated - javascript

Is there a short way of adding the translate to the text's transform attr without removing the rotation?
currently I add rotation when the text is created:
.attr('transform', rotate(270 xValue, yValue);
Note that this is done in a for loop since there are numerous texts
but later on the user should be able to drag the text horizontally. Dragging one text should move all the texts across. Doing the following removes the rotate attribute from above:
svgContainer.selectAll("text").attr("transform", "translate(" + [tx, ty] + ")");
is there a short way where I can select all the text and just append the translate attr to the rotate? Or do I need to loop through all the Texts and manually change each one?

I have just came to a working solution:
I've placed all the text inside a "g" element. and then translated the "g" element.

Related

D3 tooltip not following the mouse

I have created a webpage with multiple charts using D3 and Object Oriented Programming.
Working code link--> https://codepen.io/mohan-ys/pen/LYLwqrK
The problem I am facing is that the tooltip is not at the mouse location, it is somewhere else.
I tried using d3.event.pageX & d3.event.pageY instead of vis.mouse[0] & vis.mouse[1] which is in the code above but it does not work.
I am getting the tooltip as shown. When the mouse is at the right end of the graph, the tool tip moves further right, it gets closer somewhere in the middle & it goes to the other side by the time the cursor is on the left end of the chart!
The page is resized, then it is a totally different behaviour!
Can anyone help get the tooltip right a the mouse pointer (top-left corner at the mouse pointer) for all graphs & even when the page is resized (the graphs scale with page resize).
The vertical line follows the mouse perfectly!, so, if there is another way of creating the tooltip instead of a div, that is also ok for me.
The underlying problem is addressed here but the answer doesn't directly solve your problem. Basically, there's a second and optional argument to d3.pointer called target such that:
If target is not specified, it defaults to the source event’s
currentTarget property, if available. If the target is an SVG element,
the event’s coordinates are transformed using the inverse of the
screen coordinate transformation matrix...
You can make use of this argument per below noting that it will break your vertical tracking line if you try and just update vis.mouse:
// mouse moving over canvas
vis.mouse = d3.pointer(event); // keep this for the vertical tracking line
vis.mouse2 = d3.pointer(event, d3.select(vis.chartLocation)); // <--- NEW VARIABLE!
Now vis.mouse2 has a relative x and y - so use them where you set the style of the div:
d3
.select(vis.chartLocation)
.selectAll("#tooltip")
.html((d, i) => {
vis.xDate = d.values[vis.idx - 1].date;
return vis.xDate.toLocaleDateString("pt-PT");
})
.style("display", "block")
.style("left", vis.mouse2[0] + "px") // use vis.mouse2
.style("top", vis.mouse2[1] + "px") // use vis.mouse2
The clue is in that your first selection is vis.chartLocation.

In D3, how does one place text inside shapes that already have text attached to them?

I am currently working on the following d3 example where I'd like to place text inside of the arcs, similar to this example. However, whenever I try to append text to anything, the text just doesn't display. I've looked at the developer console, and it appears to be there, but it won't visually display on the screen. I used all the code provided in the first example, except I tried to add the following the the arc elements:
("the d3 element").enter().append("svg:text").text("???")
("the d3 element").enter().append("text").text("???")
("the d3 element").append("svg:text").text("???")
("the d3 element").append("text").text("???")
Aside from cutting off some of the styling changes, it seems like no matter where I put any of this code, it just doesn't want to work for me. I would appreciate and help!
"whenever I try to append text to anything, the text just doesn't display": Text cannot be appended to most svg elements. You can append text to the svg itself or a g, but you cannot append it to a path, rect, circle, etc.
One of the most common methods of dealing with this is to use g elements to place a shape and text while binding data to the g. Using a transform on theg will translate shape and text - great for things like circles and rectangles.
There are several other approaches you can use to overlay text on svg elements:
Use the positioning attributes of an element to set the x and y attributes of text so that you can place text over top of an element.
Use a path as a text path to place text (as in your example)
Use utility methods such as centroid (for arcs or geopaths for example)
Find the bounding box of elements and place elements using this information.
These options help place, but won't make sure that the text falls within the bounds of a shape - that is different complication.
For arcs, one option is to use a circular path as a text-path where the circle has a radius between that of the inner and outer radius of your arc - then place the text using a text offset that reflects the start angle - or make an arc for each piece of text. The general mechanism is shown below (note it can't use a circle element as svg textPaths must follow paths):
var svg=d3.select("body")
.append("svg")
.attr("height",400)
.attr("width",400)
.attr("transform","translate(200,200)")
var arc = d3.arc()
.innerRadius(50)
.outerRadius(100)
.startAngle(0)
.endAngle(2);
var arcText = d3.arc()
.innerRadius(75)
.outerRadius(75)
.startAngle(0)
.endAngle(2);
var arc = svg.append("path")
.attr("d",arc)
.attr("fill","steelblue")
var textPath = svg.append("path")
.attr("d",arcText)
.attr("id","textpath")
.attr("fill","none")
.attr("stroke","black");
var text = svg.append("text")
.append("textPath")
.attr("xlink:href","#textpath")
.text("title")
.attr("startOffset", "25%") // the bottom of the arc is from 50%-100%, the top from 0 to 50%
.style("text-anchor","middle")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
The above mechanism is very similar to the example you link to - it appends text using the arcs as text paths. The example you reference uses text paths of the visible arc(donut) segments themselves - and offsets the x,y positions to move the text into the arc itself (as opposed to on the arc).
Note that I've used v4, as opposed to v3 as in the linked example.

Align text node centrally(horizontally) inside group element

I have this group element and a text node inside it:
var legend = svg_container.append('g');
var legend_text = legend.append('text').attr('y', 0)
//other elements inside 'g'
This is just part of the code. In actuality, I have other child nodes inside the group element created on the fly after the text node(these other elements decide the width of the group element).
How do I position the text node centrally inside the group element?
To center it, you can use the text attribute text-anchor="middle" with the x and y attributes for positioning:
legend_text.attr("text-anchor","middle");
legend_text.attr("x", 0);
legend_text.attr("y", 0);
Here, your text will be fixed to the center origin of your group legend.
In fact, it won't be perfectly centered, to do so, you should set y to the half of the font-size attribute of your text.
Hoping I've correctly understood your problem !
EDIT: With your indication, I see two solutions :
My favorite one would include a modification of the legend group, by adding him a transform="translate(x,y)" attribute,
where the x and y are the center of your svg element. This one may not work with the rest of your code, but I find that doing so in general is a clean way to have multiple drawings living together in a single svg.
The other one is simplier by far, and I think will answer to your problem for now :
You replace the x and y values by the center of your legend group. The little problem here is the calculation of thoses values, because you can't read them directly from the element.
In fact, a g element doesn't have width or height attributes, it's only a container for applying attributes for a group of svg elements. That's why I recommend using my first solution.
Another way of centering it in the group using bbox
//make a g
var svg = d3.select("body").append("svg").attr("width", "900")
.attr("height", "900").append("g");
//add circle to it
svg.append("circle").attr("cx", 50).attr("cy", 50).attr("r", 30);
svg.append("circle").attr("cx", 250).attr("cy", 150).attr("r", 30);
//get bbox
var bb = svg.node().getBBox();
var centery = bb.y + bb.height/2;
var centerx = bb.x + bb.width/2;
svg.append("text").text("Hello").attr("x",centerx).attr("y", centery)
Working code here

Concurrency transitions in D3 (circle following filling path)

I am trying to create simple animated gauge charts, with red points on the end of each circle. Whole example is in here https://jsfiddle.net/yhLch8fc/3/ (please do not solve text position, it's not a problem). I tried to search on stack overflow, but tutorials like Change starting point of alongPath animation are not helping me, because my path is translated by X,Y and rotated. I thing the key part of code is:
circle.transition().duration(_duration)
.attrTween('cx', arcCircleX(_myPath.node()))
.attrTween('cy', arcCircleY(_myPath.node()));
you can easy switch to similar version (code from stack overflow above):
circle.transition().duration(_duration)
.attrTween('transformation', circleTween(_myPath.node()));
I don't know why, but it seems the red circle goes to the left side. Maybe it's because the original path is translated and rotated.
(The original version was, when I try to follow filling path, but it does not work either.)
Instead of rotating the hover path, rotate the g element that contains both the path and the circle:
enter.append("g").attr("class", "hover")
.attr("transform", "translate(" + _width / 2 + "," + _width / 2 + "), rotate(-90)");
Then:
circle.transition().duration(_duration)
.attrTween('transform', circleTween(_myPath.node()));
Here's an example. Is that what you were after?

How can I move dynamically a gridLine with mouse in D3.js?

I would like to know,
how can I move a gridLine up/down dynamically with the mouse in d3.js ?
Thanks a lot :)
The way I have done this in the past is to add an extra horizontal or vertical line to your graph and mark it as hidden.
Then whenever an element is moused-over show the line, whenever the element is moused-out hide the line again. You would need to set the X and Y values of the line such that it matches the location of the element the cursor is hovering over.
This is similar to the way showing/hiding tooltips work: https://gist.github.com/biovisualize/1016860 except you would not use a div (you would use a line) and would not use the location of the mouse pointer (you would use the x and y of the element).

Categories