Im creating a mekko chart and wanted to rotate the legends on x axis.
Example of what i did: https://codepen.io/fabioTester/pen/JjYeJEv
I want to rotate the elements with class "labelTitle" on the example...
I tried using the following code to rotate:
// rotation code
svg.selectAll(".month .labelTitle")
.attr("transform", function (d) {
return "translate(0) rotate(-25)"
});
I'm guessing my issue is the calculation of the translate, but can't figure out how to fix it.
Thanks in advance for any suggestions.
I noticed the labels seem to rotate around a point that is quite far away from their actual position, so a small increase in rotation would quickly rotate them out of sight.
If you set the transform-origin of every individual label to its x and y position, it will rotate the individual labels around that point instead.
svg
.selectAll(".month")
.append("text")
.text(function (d) {
return d.key;
})
.attr("x", 5)
.attr("y", function (d) {
return height - (margin * 2);
})
.attr('transform-origin', `5 ${height - (margin * 2)}`)
.attr("class", "labelTitle");
svg.selectAll('.labelTitle')
.attr('transform', d => 'translate(0, 10), rotate(25)')
I also noticed the y-value of your labels didn't respect the margin, so I fixed that as well.
I came up with the following codepen: https://codepen.io/pitchblackcat/pen/OJyawaV
It seems like you forgot to append deg to rotate's value.
Try this:
// rotation code
svg.selectAll(".month .labelTitle")
.attr("transform", function (d) {
return "translate(0) rotate(-25deg)"
});
Related
I already have a donut chart in d3.js.
The animation for the labels is some thing like this right now :
starting point of labels : the labels are all in the center of the donut
ending point : they end up going behind the arcs.
Now below is what i am trying to achieve....
what i want to achieve :
I want to change the starting point of labels.
I want the labels to appear from behind the arcs of the donuts instead of
the center.
The ending point is fine like it is now.
Is there a way i can achieve this ?
i.e change the emission point of the labels instead from the center they should
appear from behind the arcs.
Here is the code that i am trying to modify :
var text=svg.selectAll('text')
.data(pie(dataset.data))
.enter()
.append("text")
.transition()
.duration(1000)
.attr("transform", function (d) {
console.log(d);
console.log(arc.centroid(d));
var c = arc.centroid(d),
x = c[0],
y = c[1],
h = Math.sqrt(x*x + y*y);
return "translate(" + (x/h * labelr) + ',' +
(y/h * labelr) + ")";
})
.attr("dy", ".4em")
.attr("text-anchor", "middle")
.text(function(d){
return d.data +"%";
})
.style({
fill:'#000',
'font-size':'11px'
});
Below is the link to the fiddle :
https://jsfiddle.net/ahc4wdjk/
In D3 transition selections, the starting value is the current atribute value. So, we start creating the texts behind the arcs:
.attr("x", function(d){
return arc.centroid(d)[0]
})
.attr("y", function(d){
return arc.centroid(d)[1]
})
I had a problem here, because your arcs don't show up at the same time. The solution was giving the texts an initial .attr("opacity", 0) and waiting a little bit (using delay(1000)).
Then, I used your code for the final position, but changing labelr to labelr = radius - 160;.
Here is the fiddle: https://jsfiddle.net/gerardofurtado/zrahm2h4/1/
I have been looking into this d3.js block Timeline with Zoom. However, I am not able to figure out how the zoom function is actually implemented. Could somebody help me understand?
Frankly, there is no zoom happening.
var brush = d3.svg.brush()
.x(x)
.on("brush", display);//this calls display function on brush event drag.
Inside display function.
minExtent = brush.extent()[0],//this give the brush extent min
maxExtent = brush.extent()[1],//this give the brush extent max
Based on the max and min of the brush filter the data:
visItems = items.filter(function(d) {return d.start < maxExtent && d.end > minExtent;});
Reset the domain with the brush's max and min.
x1.domain([minExtent, maxExtent]);
Select all rectangles in the upper area not having the brush associate data to the DOM.
update it with the new scale values
rects = itemRects.selectAll("rect")
.data(visItems, function(d) { return d.id; })
.attr("x", function(d) {return x1(d.start);})
.attr("width", function(d) {return x1(d.end) - x1(d.start);});
create any new rectangles if the data is present but DOM is not present.
rects.enter().append("rect")
.attr("class", function(d) {return "miniItem" + d.lane;})
.attr("x", function(d) {return x1(d.start);})
.attr("y", function(d) {return y1(d.lane) + 10;})
.attr("width", function(d) {return x1(d.end) - x1(d.start);})
.attr("height", function(d) {return .8 * y1(1);});
Remove all the rectangle outsside the brush extent or not in the filtered item list visItems
rects.exit().remove();
Exactly the same for labels as done for rectangles above.
Hope this clears all your doubts.
I'm not sure but I think this is just a trick with D3 scales.
What happens is that it gets the selection below (which is a projection a 100% of with from time 0 to time 100) and plots into a new scale from time 50 to time 80 with the same width.
This will make the scale change in a way that looks like you zoomed on that time moment in time.
After a while I finally figured how to scale data in d3 linear.
But if you look at the following screenshot you might notice that numbers do not seem to scale appropriately:
So: e.g. 4.55 and 16.2 are almost same length, which they shouldn't be (also 4 should be much closer to the left as I scale between 0 and 565)
so this is how I create the linear scale:
var scaledData = d3.scale.linear()
.domain(d3.extent(data, function(d){return d.Betrag;}))
.range([0, width-35]);
and this is how I draw the bars:
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", function(d) { return scaledData(Math.abs(Math.round(d.Betrag))); })
.attr("height", barHeight - 1);
bar.append("text")
.attr("x",function(d) { return scaledData(Math.abs(Math.round(d.Betrag)))+3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return Math.abs(d.Betrag); });
});
I have tried around different things with this part scaledData(Math.abs(Math.round(d.Betrag))) but no matter what I do it doesn't do what I think it should... Perhaps as a newbie I still don't understand the scales thing completely... What I figure is that the problem comes from how I set the domain. if I replace the d3.extend function by [0,2000] it's all good...
You'll have to show your data to know for sure, but I think you have negative values in your data, so your domain looks like it goes from about [-600, 1800]. But when you calculate your width you first take the absolute value of your data, so your lowest possible value is 0. The solution is in your d3.extent accessor function, to evaluate the absolute value of your data (if that's actually what you want).
(EDIT: viewBox and preserveAspectRatio are great, but aren't rendering properly in IE9, so I can't use that.)
I've been working on resizing this heatmap (here it is on a block). By selecting the width of a div, appending an SVG to it, and setting my SVG's width to that of the div, I've been able to make the heatmap responsive.
Nevertheless, I've run issues when I've tried to update the heatmap on resize. Here's the resize function:
d3.select(window).on("resize", resize);
function resize(){
marginHM = {top:35, right:0, bottom:50, left:30};
divWidthHM = parseInt(d3.select("#chartHM").style("width"));
widthHM = divWidthHM-marginHM.left-marginHM.right;
gridSize = Math.floor(widthHM/24);
legendElementWidth = gridSize*2.665;
heightHM = (9*gridSize)-marginHM.top-marginHM.bottom;
svgWidthHM = widthHM + marginHM.left+marginHM.right;
svgHeightHM = heightHM+marginHM.top+marginHM.bottom;
svgHM.select("svg").style("width", (svgWidthHM+20) + "px")
.style("height", (svgHeightHM+30) + "px");
dayLabels.attr("y", function (d, i){ return i*gridSize;})
.attr("transform", "translate(-6," + gridSize/1.5+")");
timeLabels.attr("x", function(d,i) {return i * gridSize;})
.attr("transform", "translate(" + gridSize/2+", -6)");
cards.attr("x", function(d) {return d.hour *gridSize; })
.attr("y", function(d) {return d.day * gridSize; })
.attr("width", gridSize)
.attr("height", gridSize);
d3.selectAll("rect.HMLegend")
.attr("x", function(d, i){ return legendElementWidth * i;})
.attr("y", 7.2*gridSize)
.attr("width", legendElementWidth)
.attr("height", gridSize/2);
d3.selectAll("text.HMLegendText")
.attr("x", function(d, i){ return legendElementWidth *i;})
.attr("y", 8*gridSize);
}
If you open the heatmap and drag the window to a wider size than it initially loaded in, you'll find that the heatmap grows, but the SVG's dimensions are cut off at the initial width; I find this really strange, considering I update the width in the resize function. You can decrease the width from its original value, and the map will size down, but you can't increase it above the original value of the width.
Additionally, I run into issues with the legend bar and legend labels. The the legend labels don't shift on window resize, and when you click back and forth between USA and New York, the legend colours often move off screen or show duplicates. Have been staring at this all evening, and am getting to the point where everything's blurring into one nonsensical line of code. Would really appreciate someone weighing in on exactly what it is I'm messing up!
I'm new to D3.js and using following example from D3.js to create a simple dashboard for one of my web application.
http://bl.ocks.org/NPashaP/96447623ef4d342ee09b
My requirement is to rotate top value labels of each bar vertically by 90 degrees.
I changed following method by adding "transform" attribute. Then the labels do not align properly.
//Create the frequency labels above the rectangles.
bars.append("text").text(function(d){ return d3.format(",")(d[1])})
.attr("x", function(d) { return x(d[0])+x.rangeBand()/2; })
.attr("y", function(d) { return y(d[1])-5; })
.attr("text-anchor", "middle")
.attr("transform", function(d) { return "rotate(-90)" });
I tried to find a solution for long time but couldn't. Links to my codes are given below.
https://jsfiddle.net/vajee555/7udmyj1k/
Can anybody please give me an idea how to archive this?
Thanks!
EDIT:
I have solved the problem here.
http://jsfiddle.net/vajee555/7udmyj1k/5/
Remember that when you rotate an element, the x and y coordinates are changed: they are no longer with respect to that of the chart, but with respect to the new rotated orientation of the element. Therefore, you will need to compute the x and y attributes differently.
By rotating -90deg, your x axis will be flipped to y, and the y will be flipped to -x:
I have made some small pixel adjustments to make it appear aesthetically pleasing, such as the +8 I have added to the y coordinate and the +5 I have added to the x coordinate, but the fine tuning is up to you.
// Create the frequency labels above the rectangles.
bars.append("text").text(function(d){ return d3.format(",")(d[1])})
.attr('transform', 'rotate(-90)')
.attr("y", function(d) { return x(d[0]) + x.rangeBand()/2 + 4; })
.attr("x", function(d) { return -y(d[1]) + 5; });
Also, change how the coordinates are calculated in the hG.update() function:
// transition the frequency labels location and change value.
bars.select("text").transition().duration(500)
.text(function(d){ return d3.format(",")(d[1])})
.attr("x", function(d) { return -y(d[1]) + 5; });
See working fiddle here: https://jsfiddle.net/teddyrised/7udmyj1k/2/
//Create the frequency labels above the rectangles.
bars.append("text").text(function(d){ return d3.format(",")(d[1])})
.attr("x", function(d) { return x(d[0])+x.rangeBand()/2; })
.attr("y", function(d) { return y(d[1])-5; })
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90,0,0)" );
Change the last line as above.