I'm using d3.js. I'm trying to rotate an SVG element 360 degrees, so that it spins once and returns to it's original position.
Rotating it 3/4 of the way like this works fine:
thing
.transition()
.attr('transform', 'rotate(270,640,426)')
.duration(6000);
But trying to animate the complete rotation does nothing:
thing
.transition()
.attr('transform', 'rotate(360,640,426)')
.duration(6000);
I think d3 (or maybe this a more general fact about svg transform attribute) sees that the end is the same as the beginning and just takes the shortcut by doing nothing. Similarly, if I do 365 degrees, it only moves +5 degrees.
A. Why is this?
B. What's the right way to do it?
D3 normalizes the SVG transforms; this is the cause for the effect you're seeing. You can do this however with a custom tween function:
function rotTween() {
var i = d3.interpolate(0, 360);
return function(t) {
return "rotate(" + i(t) + ")";
};
}
Complete example here.
Related
I've just started learning d3. I've made some progress on learning, but I've run into something I haven't been able to figure out on my own.
Here is what I have so far: http://tributary.io/inlet/83fba4500986b4638326
What I've been trying to figure out how to do is fade in the data points as the line path animates through them. The best idea I had was dividing the transition time by the number of points and then have the delay for each data point be decided by that, but I had trouble getting that working properly.
Is there a reasonable way to do this?
P.S. I also seem to have lost my y-axis labels and am not sure why... any ideas?
Thanks for your time and help!
So let's start by the easy part. The Y axis is missing because your svg elements have an overflow:hidden and the second svg element is stuck to the top left of corner of the first one. some x,y space plus overflow:auto solve the problem.
For fading the circle when the path go trought it, I don't think you can do that with one transition. So, a solution, because the path drawing works on offset from total length to 0, you can calculate the distance between each circle and "transition" the path from circle to circle, fading it at the end of the transition. To do so, get the circles coordinates, calculate the distances, and set the transition loop.
//Get coordinates
svg.selectAll("circle").each(function(){
coordinates.push({x:d3.select(this).attr("cx"),y:d3.select(this).attr("cy")});
});
//Get Distances
for(var j=0;j<coordinates.length-2;j++)
{
var a,b,distance;
a= coordinates[j];
b= coordinates[j+1];
distance = Math.sqrt((Math.pow(b.x-a.x,2))+(Math.pow(b.y-a.y,2)));
//console.log("j:" + j + " a: " + JSON.stringify(a) + " b: " + JSON.stringify(b) + " distance: " + distance);
distanceData.push(distance)
}
//Loop transition
var counterTransition =0,currentLength=distanceData[counterTransition];
path
.attr("stroke-dasharray", totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(500)
.ease("cubic")
.attr("stroke-dashoffset",totalLength-currentLength)
.attr("class", "line").each("end",animeNext);
function animeNext(){
counterTransition +=1;
if(counterTransition<=distanceData.length)
{
var circle =svg.selectAll("circle")[0][counterTransition];
circle=d3.select(circle);
circle.attr("opacity",0.5);
currentLength += distanceData[counterTransition] ;
path
.transition()
.duration(500)
.ease("cubic")
.attr("stroke-dashoffset",totalLength - currentLength)
.attr("class", "line").each("end",animeNext);;
}
Example : http://tributary.io/inlet/e2eab2e689479008f11c
I simplified the data generation for the sake of the test. Removed the draw on click. It looks like that the code executes 1 to 3 times randomly but I think it is tributary.
You can play with the duration to improve transition smoothness.
Hope it helps!
EDIT: Other solution with one transition, using a short interval(100ms) and checking if the path's stroke-dashoffset is equal or greater than each circle's distance and if so fade the circle. Clear the interval at the end of the transition.
I've generated a D3 visualization (a force directed graph) that requires zooming and panning. I've got 2 problems however when it comes to zooming, and I can't find any decent examples on how I might overcome these problems:
The first problem is I've followed all the examples I can find about zooming, which involves adding groupings and adding a rectangle to ensure that the entire area is zoomeable. If I style the rectangle a slightly opaque blue then I get SVG that looks like this when I zoom out:
The problem with this is that I can zoom in/out absolutely fine while I've got my mouse over the blue rectangle area. The problem is I want this to be fully opaque, which means that when I zoom right out, it's very easy to place the cursor outside of this box and then you're unable to zoom in. Is there a way I can make the SVG itself zoomeable or pick up on these events?
This is how I go about generating the various layers and the zoomed function:
function zoomed() {
group2.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
svg = d3.select(target)
.attr("pointer-events", "all")
.append("svg");
group = svg.append('svg:g')
.call(d3.behavior.zoom().on('zoom', zoomed))
.on("dblclick.zoom", null);
group2 = group.append("g");
rect = group2.append('svg:rect')
.style("opacity", 0.3)
.attr('width', width)
.attr('height', height);
The second problem I have is that I'm trying to automatically size my text based on this http://bl.ocks.org/mbostock/1846692 example. When I've tried this however I seem to be getting text that renders really poorly. It seems to suffer from:
Being difficult to read
Not appearing contained within the circle
Being so small the entire thing compresses (image 2)
var texts = planets.append("text")
.text(function(d) { return d.name; })
.style("font-size", "24px") // initial guess
.style("font-size", function(d) {
return Math.min( 2 * d.size, (2 * d.size - 8) / this.getComputedTextLength() * 24) + "px";
})
.attr("dx", function(d) { return -d.size; })
.attr("dy", ".35em")
.style("fill", "white");
I thought that SVG would just handle this, I understand that some of the font-sizes can come out small, but if you zoom in should that not all sort itself out?
I've got a JSFiddle http://jsfiddle.net/IPWright83/vo7Lpefs/22/ to demonstrate.
I've not yet managed to work out a resolution to my first issue (regarding the zooming box) however I did manage to track down the text rendering issue.
This was actually because the each circle/node had a stroke property to provide the white border. This was also applying to the text element, and when the font was very small the stroke was much larger than the overall fill of the text. Removing the stroke from the text elements ensured that they rendered even when very small.
I'm using D3 Drag to drag some circles around the canvas. I then have them with fixed positions. However, I wish to position them from a distance from another svg element that I have created:
node.attr("transform", function(d,i) { return "translate ("+ arc[i].centroid() +")" +"translate("+d.x+","+d.y+")"; })
When dragged the circles do not drag until multiple clicks of the mouse & is usually far from the actual mouse hand.
I know I can get rid of the centroid, but would like to know a work around this problem.
http://jsfiddle.net/Zc4z9/10/
The jump you are getting on the first drag attempt results from the fact that the translate on line 57 doesn't match the one further down. To avoid the jump, make lines 57 and 76 match. Change 57 to:
d3.select(this).attr("transform", "translate ("+ arc[i].centroid() +")" +"translate("+d.x+","+d.y+")");
To get this to work, I had to pass d and i as parameters in line 52:
.on("drag", function(d, i) {
Working fiddle: http://jsfiddle.net/Zc4z9/11/
I would like to create a mashup of the functionalities as seen from
http://bl.ocks.org/4063423 and http://philogb.github.com/jit/static/v20/Jit/Examples/Sunburst/example2.html
I would like to use d3.js or at least a pure javascript solution but a solution that will respond to mouse clicks to display more information about the selected section.
Zooming in and out is not mandatory, but if I can achieve it, it will be good.
Now my question, Is there a framework that can support this or do I have to mash them up on my own.
Disclaimer: google was not that helpful!
It is easy to do with D3 alone: http://bl.ocks.org/4678148 If you click any element, the element will be focused and transitioned to 90 deg with the selected class set on it.
Also, the legend text on the top right changes to the name of the element selected. The part of code which achieves this coupling is:
d3.selectAll("path").on("click", function (d, i) {
var newAngle = - (d.x + d.dx / 2);
innerG
.transition()
.duration(1500)
.attr("transform", "rotate(" + (180 / Math.PI * newAngle) + ")");
// Set the class "selected" on the chosen element.
path
.classed("selected", function (x) { return d.name == x.name; });
// Update the text box with the right context
// This can be arbitrarily complex to show as many details about the
// object as required.
textBox.data(["Clicked: " + d.name])
.text(String);
});
Update
For the zoomable behavior such that the clicked element transitions to the center, you can use almost the same code as used as here or here. I have made small changes to the code to show how to extract information about which item was clicked: http://bl.ocks.org/4747342
The change in code required is simpler than before:
d3.selectAll("path").on("click", function (d, i) {
// Zooming
path.transition()
.duration(750)
.attrTween("d", arcTween(d));
// Update the text box with the right context
// This can be arbitrarily complex to show as many details about the
// object as required.
textBox.data(["Clicked: " + d.name])
.text(String);
});
In the following D3 sunburst :
http://jsfiddle.net/maxl/eabFC/
.attr("transform", function(d) {
return "rotate(" + (d.x + d.dx / 2 - Math.PI / 2) / Math.PI * 180 + ")";
});
The labels in the left quadrants are upside down, I would like to perform a rotation on them so that the text reads from left to right.
The transformation should only apply to the arcs from approximately 100 degree to 270 degree.
Following this example: http://www.jasondavies.com/coffee-wheel/
I've edited your jsfiddle here: http://tributary.io/inlet/4127332/
You are going to have to deal with your long labels and the above example shows how to do multi-line.
Also note that you are using an old version of d3, it is no longer necessary to call d3.layout separately. Here is the link to new shiny version of d3:
<script src="http://d3js.org/d3.v3.js"></script>