I am trying to do a radial area plot with a colour gradient to reflect this. However the gradient does not appear to ever want to sit perfectly in line / at the correct side with the circle (see image below).
I have tried constraining the width and hight of the svg so that they are equal, but with no avail.Every refresh (with random data), the central ring for the gradient will shift and warp into a different shape, but never lie on the mean line where it should be.
I have ensured that the svg is square
var width = window.innerWidth , height = window.innerHeight;
width = height = d3.min([width,height])
var data = d3.range(0,100).map(d=>Math.random())
Given the gradient the following properties
var linearGradient = defs.append("radialGradient")
.attr("id", "rad-gradient")
.attr("cx", "50%") //The x-center of the gradient, same as a typical SVG circle
.attr("cy", "50%") //The y-center of the gradient
.style("r", "50%");
and my area mathematically as
var area = d3.area()
.x1(function(d,i) { return width/2+Math.cos(i\*angle)*(h(d)) }) .x0(function(d,i) { return > width/2+Math.cos(i\*angle)*(h(mean)) }) .y0(function(d,i) {> return height/2+Math.sin(i\*angle)*(h(mean)) }) .y1(function(d,i) { return height/2+Math.sin(i\*angle)*(h(d)) })
And appended the created gradient to the svg path
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area)
.style("fill", "url(#rad-gradient)")
The solution lies in creating a circle and filling it with the correct gradient. This ensures that the radial gradient will always be centered around the origin.
You can then use clipPath to extract only the path you wish to use from the circle. After a little fiddling to align the gradient radius, we get the desired effect.
// previous definition for area path appended as a clip path
clip = defs.append("clipPath")
.attr('id','clip')
.append("path")
.data([data])
.attr("class", "area")
.attr("d", area)
//extracting this path from a circle with the desired gradient
svg.append("circle")
.attr("cx", width/2)
.attr("cy", height/2)
.attr("r", rmax)
.attr("clip-path","url(#clip)")
.style("fill", "url(#linear-gradient)")
Related
Suppose we wanted to make a list-like visual. Setting the y logic for the circles can be as simple as:
var data = [0,1,2,3,4,5,6,7,8,9];
var yScale = d3.scaleLinear()
.range([height,0])
.domain([0,9]);
svg.selectAll(null)
.data(data)
.enter()
.append('circle')
.attr('cy', function(d) { return yScale(d) })
.attr('cx', 100)
.attr('r', 10)
.style('fill', "#a6a6a6");
However, suppose we wanted to go for some style points and arrange the circles not in a blocky / tabular arrangement but rather arrange them about a circle or arc. I had this result in mind (only concerned with the outer circles):
While I think d3 does have trigonometric functions, I have never seen them used in pixel coordinates. I'd imagine the pseudo-code to be something like:
var semiCircleScale = d3.?????
.range([250 degrees, 110 degrees])
.domain([0,9]);
svg.selectAll(null)
.data(data)
.enter()
.append('circle')
.attr('cy', function(d) { return semiCircleScale(d) })
.attr('cx', 100)
.attr('r', 10)
.style('fill', "#a6a6a6");
Question
Is anyone familiar with using circle / arc scales for use with x,y logic for appending shapes? Or is there an easier/less-math-intensive way?
So the idea is to create 2 different path of arc and then calculate the circumference and place the circles along with.
d3.svg.arc()
.append("path")
.attr("d", arc1)
Here is a fiddle link with minimum code to establish the idea
https://jsfiddle.net/Dibyanshu/g03p6sxj/
I'm trying to remove the size update of my line when i zoom on my svg.
Basically my line is like that
points = [[this.x(first.longitude), this.y(0)], [this.x(first.longitude), this.y(first.altitude)]];
this.line[i] = this.chart
.append('path')
.attr('d', curveafter(points))
.attr('p', id.segmentName)
.attr('stroke', "black")
.attr('fill', 'none')
.on('click', this.filter)
.attr("stroke-width", 1)
and when i'm zooming, i'm updating the position of the line like that
updateChart(d) {
var newX = d.transform.rescaleX(this.x);
var newY = d.transform.rescaleY(this.y);
this.xAxis.call(d3.axisBottom(newX))
this.yAxis.call(d3.axisLeft(newY))
this.line.forEach((line) => {
line
.attr("transform", d.transform)
.attr("stroke-width", 1)
}
)
},
But when i'm zooming, the size of my line increases, and when I zoom out it decreases.
How do I block the stroke-width (size) and not move it no matter what I do?
You have 2 options:
Don't put your <line> under transformed <g>. When you handle a zoom event, recalculate the line endpoint coordinates and update the <line> attributes (x1,y1,x2,y2)
Set the line's stroke-width according to the zoom factor: strokeWidth = 1 / e.transform.k (I don't like this option but it can work)
I am trying to create a simple line graph using d3 which segments the curve and paint each segment with a different colour. Currently I am only able to colour the whole area under the curve.
Current:
Attempting to achieve (Pardon me for the terrible colouring. In a rush for time):
This is bits of relevant code. Please help!
var x = d3.scaleLinear();
var y = d3.scaleLinear();
//Set the range of the data
x.domain([0, Math.max(endGrowth, endPlateau) ,maxX]).range([0, width*0.8, width]);
y.domain([0, maxY]).range([height, 0]);
//Define the area under the Graph
var area1 = d3.area()
.x0(function(d){
return x(d.Rank);
})
.y1(function(d){ return y(d.Elements);})
.y0(height);
//Add the colored regions
svg.append("path")
.data([data])
.attr("class", "areaUnderGraph")
.attr("fill", "blue")
.attr("transform", "translate(" + leftMarginLabel + ",0)")
.attr("d", area1);
Right now, the area under the curve is one path, so it can only be colored one color. The simplest way to color different portions under the curve different colors is to split them up in data. It's not clear where data is coming from, but you'd take sub-sections of the array, like
var segments = [data.slice(0, 2), data.slice(2)];
svg.selectAll("path")
.data(segments)
.enter()
.append("path")
.attr("fill", function(d) { /* use d to choose a color */ })
That's the gist: you'd have multiple slices of the data, and instead of one path, you'd create multiple paths that you can color as you wish.
I have a simple pie chart made with d3js and I would like to add transparent gap between each path.
paths = pieWrap.selectAll("path")
.data(pie(data)).enter()
.append("path")
.style("fill", "rgba(90, 168, 217, 1)")
.style("stroke", "#FFF")
.style("stroke-width", "1")
.style("stroke-opacity", "0")
.attr("d", arc);
Example here : http://jsfiddle.net/x4p0eLmL/2/
Just to know, in my case the background is an image so I can't use its color.
I tried stroke-opacity but it doesn't seem to work.
Is there a proper way to do that with d3js?
Thanks
I have had the same idea as #redress suggested: http://jsfiddle.net/x4p0eLmL/9/. The added part is as follows:
.attr("transform", function(path) {
middleAngle = -Math.PI/2 + (path.startAngle+path.endAngle)/2;
dx = 3 * Math.cos(middleAngle);
dy = 3 * Math.sin(middleAngle);
return "translate("+dx+", "+dy+")";
})
path has the attributes startAngle and endAngle. There is computed middle angle and translated. It is suitable for smaller gaps between the paths. Each path is translated to outer circle with radius+3 in the current example. There is the problem with "wider" gaps where the circle may be "distorted"
I wanted to create a doughnut chart with d3.js and I want to put a navigation button just exactly on the middle of it, maybe when clicked shrinks the whole chart into a smaller version (expand probably). But that is not how complex my question is, but if you can find an example that probably exactly what I wanted to have, its better.
The question now is just position a circle, I don't know if there is a better alternative for it, but this is how it goes.
var height=800,
width=800;
var data = [10,50,80];
var color = d3.scale.ordinal()
.range(["red", "blue", "yellow"]);
// the graph
var radius=300;
var canvas = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var group = canvas.append("g")
.attr("transform", "translate(300,300)");
var arc = d3.svg.arc()
.innerRadius(100)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d){
return d;
});
var arcs = group.selectAll(".arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.attr("fill", function(d){
return color(d.data);
});
canvas.append("g")
.attr("class", "collapse")
.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 100);
I just a did a dirty append of circle, because I don't want to go on, if there is a better way to do it, that is why I asked. How would one make it to the center, and if you are to generous, how to add events on it, probably using on, and then scale it down to about 100px and make it the button itself, so when toggled again, it collapses
EDIT
Guess I'm a bit asking to much, how about:
Center the button without hard coding 300px to it? and on your opinion , would an svg circle can do fine as a button?