I'm following this zoom example. In my case, I don't know how to redraw my data for my svg.
In the example, the svg is initialized like this
chartBody.append("svg:path")
.datum(data)
.attr("class", "line")
.attr("d", line);
var line = d3.svg.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.value);
});
And is redrawn like this in function "zoomed"
svg.select(".line")
.attr("class", "line")
.attr("d", line);
While in my case
Init:
userSvg.selectAll("circle")
.data(userNodes.slice(1))
.enter().append("svg:circle")
.on("click", function(d){ console.log(d.ind); })
.on("mousemove", function(d){ brushOnUser(d.ind); })
.on("mouseout", function(){ brushOnUser(); })
.attr("r", function(d) { return d.radius; })
.attr("cx", function(d, i) { return userNodesScaleX(d.x); })
.attr("cy", function(d, i) { return userNodesScaleY(d.y); })
.style("fill", function(d, i) { return 'gray'; });
Redraw:
userSvg.selectAll("circle")
.attr("class", "circle");
Of course this redraw doesn't work.
So how do I redraw this?
In the redraw function, you need to set all the attributes that are changed. For a line, this is basically only the d attribute, as it contains all the information that determines how the line is drawn. For a circle, this would be the position of the circle and the radius. That is, your redraw function would look something like this.
userSvg.selectAll("circle")
.attr("r", function(d) { return d.radius; })
.attr("cx", function(d, i) { return userNodesScaleX(d.x); })
.attr("cy", function(d, i) { return userNodesScaleY(d.y); });
Depending on what you're changing, you may have to set a different set of attributes. That is, if you're not changing the radius, then there's no need to set that, but if you're changing the fill color, you would need to set that as well.
Related
I am working on a line chart based on the tutorial https://datawanderings.com/2019/11/01/tutorial-making-an-interactive-line-chart-in-d3-js-v-5/
For the code block below, I want to add points every three rows (every three days):
//---------------------------POINTS-----------------------------//
lines.selectAll("points")
.data(function(d) {return d.values})
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d.date); })
.attr("cy", function(d) { return yScale(d.measurement); })
.attr("r", 10)
.attr("class","point")
.style("opacity", 1);
I followed the link Add a Circle for Every nth Data Element d3.js, and tried to change it to
//---------------------------POINTS-----------------------------//
lines.selectAll("points")
.data(slices.filter(function(d,i) {return i%3 == 0}))
.enter()
.append("circle")
.attr("cx", function(d, i) { return xScale(d.date); })
.attr("cy", function(d, i) { return yScale(d.measurement); })
.attr("r", 10)
.attr("class","point")
.style("opacity", 1);
However, all the points appears to locate at the left top corner of the graph.
May I ask what I did wrong, and how can I modify my code to make the circles to locate on the lines as showing the circles every three days? Thank you!
My D3 circle pack looks like this: (also accesible via jsfiddle)
However, I would like the diagram to look like this: (don't pay attention on labels, or circle pack placement, they are not essential for my case; I meant just co show "3d" looks of circles, and their coloring)
What would be the good way to achieve this?
After #Delapouite answer, I put together another jsfiddle:
The key code is:
var data2 = pack.nodes(data);
var grads = svg.append("defs").selectAll("radialGradient")
.data(data2)
.enter()
.append("radialGradient")
.attr("gradientUnits", "objectBoundingBox")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", "100%")
.attr("id", function(d, i) { return "grad" + i; });
grads.append("stop").attr("offset", "0%").style("stop-color", "white");
grads.append("stop").attr("offset", "100%").style("stop-color", "navy");
and
var circles = vis.append("circle")
.attr("stroke", "black")
.attr("fill", function(d, i) {
return !d.children ? "url(#grad" + i + ")" : "beige";
})
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r; });
You can fake the 3D effect of each ball by applying a soft radial gradient to the fill property of the circles :
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Gradients
I have JS objects like these:
{
name : "sldjfvbsdolbobgsd",
myWeight : "[1,65,34,4,6,7,4,4,7,8,5]"
}
For each such object, there is a circle in an SVG element in force-directed layout.
'i' is a global variable. I am displaying the 'name' element of each object like this currently:
node.append("text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function (d) { return d.name; });
I want to display both the name and the current weight of each object. I tried something like this but it didn't work:
*node.append("text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.html(function (d) { return d.name; } + "<br />" + function (d) { return d.myWeight[i]; });*
The output for every node is like this:
function (d) { return d.name; }function (d) { return d.myWeight[i]; }
whereas I want:
sldjfvbsdolbobgsd
34 // say, if `i` is 2
Please help me with displaying it correctly.
The reason your code doesn't work is because d3 expects the argument passed to .html to be either a function or a constant. In your case the + causes the functions to be converted to strings. So the constant that you pass to .html ends up being:
function (d) { return d.name; } <br /> function (d) { return d.myWeight[i]; }
However, the SVG text element does not render the <br/> tag and so you don't get the line break.
The proper way to add line breaks inside SVG text is to use tspan with dy offets:
var text = node.append("text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em");
text.append("tspan")
.attr("dy", 0)
.attr("x",0)
.text(function(d) {return d.name;});
text.append("tspan")
.attr("dy", "1.2em") // offest by 1.2 em
.attr("x",0)
.text(function(d) {return d.myWeight;});
I have code here and I need to add text into the rect element on the right of all the hexagons and have it act like a paragraph inside of a div with a given height and width and have it wrap properly. Here is the jsfiddle. and the code that draws the rect is below:
http://jsfiddle.net/2nykc/
var infoRect = svgContainer.append("rect");
var infoRectAttr = infoRect.attr("x", function(){
return infoDivX;
})
.attr("y", function(){
return infoDivY;
})
.attr("width", function(){
return infoDivWidth;
})
.attr("height", function(){
return infoDivHeight;
})
.style("fill", "rgb(30,180,134)")
.style("stroke", "black")
.style("stroke-width", 3)
.attr("id", "infoRect");
I am trying to apply the colors from the color = d3.scale.category10(); var to the gradient for the circle svg, what am I doing wrong? All I am seeing is the first color of the color = d3.scale.category10();(which is blue) to 0% opacity gradient but that is all. If I take the gradient out then I see the range I want which is from 1-4? Thanks in advance!
var nodes = d3.range(300).map(function() { return {radius: Math.random() * 12 + 4}; }),
root = nodes[0],
color = d3.scale.category10();
root.radius = 0;
root.fixed = true;
var force = d3.layout.force()
.gravity(0.05)
.charge(function(d, i) { return i ? 0 : -4000; })
.nodes(nodes)
.size([width, height]);
force.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", width)
.attr("height", height);
var gradient = svg.append("defs").append("radialGradient")
.attr("id", "gradient")
.attr("cx", "50%")
.attr("cy", "50%");
gradient.append("stop")
.attr("offset", "75%")
.style("stop-color", function(d, i) { return color(i % 4); })
.attr("stop-opacity", "1");
gradient.append("stop")
.attr("offset", "100%")
.style("stop-color", function(d, i) { return color(i % 4); })
.attr("stop-opacity", ".1");
svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr("r", function(d) { return d.radius; })
.style("fill", "url(#gradient)");
Your stop elements don't have any data joined with them, so in your function (d, i), i will always be 0. If you just want the two stops, you could do something like this:
gradient.append("stop")
.attr("offset", "75%")
.style("stop-color", color(0))
.attr("stop-opacity", "1");
gradient.append("stop")
.attr("offset", "100%")
.style("stop-color", color(1))
.attr("stop-opacity", ".1");
If instead you're just trying to fade the edges of your circles, a gradient isn't what you want at all. Instead, you'll need to apply a solid color to each circle, then create a single opacity-only gradient inside a mask, and apply that mask to each circle. Something like this:
var defs = svg.append('defs');
var gradient = defs.append('radialGradient')
.attr('id', 'fadient');
gradient.append('stop')
.attr('offset', '75%')
.attr('stop-color', 'white')
.attr('stop-opacity', 1)
gradient.append('stop')
.attr('offset', '100%')
.attr('stop-color', 'white')
.attr('stop-opacity', .1)
var mask = defs.append('mask')
.attr('id', 'mask')
.attr('maskContentUnits', 'objectBoundingBox')
.append('circle')
.attr('fill', 'url(#fadient)')
.attr('cx', .5)
.attr('cy', .5)
.attr('r', .5)
svg.selectAll("circle")
.data(nodes.slice(1))
.enter().append("circle")
.attr('cx', function (d, i) { return 20 * i })
.attr('cy', 50)
.attr("r", function(d) { return d.radius; })
.attr('mask', 'url(#mask)')
.attr("fill", function (d, i) { return color(i); });