I have a radar chart with polygons. Now I want them to be filled with color on mouseover, but only when the mouse is on the path. When the mouse is inside the polygon it should have no fill.
So far I tried
svg.selectAll('.polygon')
.data(scaledData)
.enter()
.append('svg:path')
.attr('d', radialLine)
.attr('stroke', function(d, i) {return colors(i);})
.attr('stroke-width', '3')
.attr('fill', 'none')
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.select(this).attr('stroke')).attr('opacity', 0.3);
})
.on("mouseout", function(d) {
d3.select(this).style("fill", "none").attr('opacity', 1);
});
Which fills when I'm over the whole polygon. Also I'd like to have the stroke stay the same and not change it's opacity.
Any help is appreciated
Set the attribute pointer-events="visibleStroke" to trigger the event over the stroke, and use fill-opacity instead of opacity.
svg.selectAll('.polygon')
.data(scaledData)
.enter()
.append('svg:path')
.attr('d', radialLine)
.attr('stroke', function(d, i) {return colors(i);})
.attr('stroke-width', '3')
.attr('fill', 'none')
.attr('pointer-events', 'visibleStroke')
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.select(this).attr('stroke'))
.attr('fill-opacity', 0.3);
})
.on("mouseout", function(d) {
d3.select(this).style("fill", "none")
.attr('fill-opacity', 1);
});
Related
I wanted to draw a pulsating warbling circle on a geomap using d3, using this example for guidance. However—and I know this is a very basic question, so apologies—I can't seem to get the selector to fire right. Here is the relevant code:
layer2
.attr("id", "locations")
.selectAll(".state")
.data(columbia.features)
.enter().append("circle")
.attr("class", "location")
.attr("r", "4px")
.attr("cx", function(d) { return proj(d['geometry']['coordinates'])[0]; })
.attr("cy", function(d) { return proj(d['geometry']['coordinates'])[1]; })
.attr("d", path)
.each(pulse);
function pulse() {
var circle = svg.select(".location");
console.log(circle);
(function repeat() {
circle = circle.transition()
.duration(2000)
.attr("stroke-width", 20)
.attr("r", 10)
.transition()
.duration(2000)
.attr('stroke-width', 0.5)
.attr("r", 200)
.ease('sine')
.each("end", repeat);
})();
}
Right now I have:
var circle = svg.select(".location");
What should I have instead of .location?
Here is the full file.
Edit: got it; I need selectAll, notselect.
I am working on a population pyramid that has an updating function.
http://bricbracs.com/hh/
As you can see the bars expand and contract in a horizontal line when you update it with new data. I want to modify the transition effect so that the bars enter and exit vertically like this:
http://vis.stanford.edu/jheer/d3/pyramid/shift.html
I have been following this tutorial and modifying the code but so far no luck.
https://strongriley.github.io/d3/tutorial/bar-2.html
Here is the code that first draws the bars on loading. (this is the male bar group, the female bar group is the same)
leftBarGroup.selectAll('.bar.left')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar left')
.attr('y', function(d) { return yScale(d.group); })
.attr("width", 0)
.attr("opacity", 0)
.transition()
.duration(500)
.attr('width', function(d) { return xScale(d.male); })
.attr('height', yScale.rangeBand())
.attr("opacity", 1)
And here is the corresponding part of the code in the updating function which changes the bars.
var sel = leftBarGroup.selectAll('.bar.left')
.attr('class', 'bar left')
.data(data)
.data(data, function(d) { return d.male; })
.transition()
.attr('y',0)
.duration(500)
.attr('y', function(d) { return yScale(d.group); })
.attr('height', yScale.rangeBand())
.attr('width', function(d) { return xScale(d.male); })
.attr('height', yScale.rangeBand())
Thanks in advance.
Here's one way to reproduce the effect in your linked example. I offset the bars and then slide them back into place. You then handle the top and bottom bars slightly different.
Note, I only did the slide down on the male side of the pyramid, if you need help going the rest of the way just leave me a comment.
var sel = leftBarGroup.selectAll('.bar.left')
.attr('class', 'bar left')
.data(data)
.data(data, function(d) {
return d.male;
})
// offset y to slide down
.attr('y', function(d){
var self = d3.select(this);
return +self.attr('y') - yScale.rangeBand();
})
.transition()
.duration(500)
// slide it back into place
.attr('y', function(d) {
return yScale(d.group);
})
// and set new width
.attr('width', function(d) {
return xScale(d.male);
});
// for the very top bar
// not only slide it but "fade it in"
leftBarGroup.select(':last-child')
.style('opacity', 0)
.transition()
.duration(500)
.style('opacity', 1)
.attr('y', function(d) {
return yScale(d.group);
});
// append a fake bar on the bottom
// to slide and fade out
leftBarGroup.append('rect')
.attr('y', function(d) {
return yScale('0-4');
})
.attr('height', yScale.rangeBand())
.attr('width', function(){
return leftBarGroup.select(':first-child').attr('width');
})
.style('fill', 'steelblue')
.style('opacity', 0.6)
.transition()
.duration(500)
.attr('y', function(d) {
return yScale('0-4') + yScale.rangeBand();
})
.style('opacity', 0)
.remove();
EDITS
Going up is just a matter of reversing the logic:
var sel = leftBarGroup.selectAll('.bar.left')
.attr('class', 'bar left')
.data(data)
.data(data, function(d) {
return d.male;
})
// offset y to slide up
.attr('y', function(d){
var self = d3.select(this);
return +self.attr('y') + yScale.rangeBand()
})
.transition()
.duration(500)
// slide it back into place
.attr('y', function(d) {
return yScale(d.group);
})
// and set new width
.attr('width', function(d) {
return xScale(d.male);
});
// for the very bottom bar
// not only slide it but "fade it in"
leftBarGroup.select(':first-child')
.style('opacity', 0)
.transition()
.duration(500)
.style('opacity', 1)
.attr('y', function(d) {
return yScale(d.group);
});
// append a fake bar on the top
// to slide and fade out
var w = leftBarGroup.select(':last-child').attr('width');
leftBarGroup.append('rect')
.attr('class','fake')
.attr('y', function(d) {
return yScale('90+');
})
.attr('height', yScale.rangeBand())
.attr('width', w)
.style('fill', 'steelblue')
.style('opacity', 0.6)
.transition()
.duration(500)
.attr('y', function(d) {
return yScale('90+') - yScale.rangeBand();
})
.style('opacity', 0)
.remove();
Updated code sample here.
Here is a plunker modified from mbostock
I want to make the text labels drag-able and attach a line to the circle when dragged.
.call(drag) works on the dots but not the labels
label = container.append("g")
.attr("class", "label")
.selectAll(".label")
.data(dots)
.enter().append("text")
.text(function(d) {return d.x + d.y; })
.attr("x", function(d) {return d.x; })
.attr("y", function(d) {return d.y; })
.attr("text-anchor", "middle")
.call(drag)
Here's a JSFiddle I made to demonstrate draggable text labels in D3.js
https://jsfiddle.net/h1n6fuwr/
Essentially you want to define the following variables/functions:
const drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended)
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
}
function dragged(d) {
const elem = d3.select(this)
elem.attr('x', +elem.attr('x') + d3.event.dx)
elem.attr('y', +elem.attr('y') + d3.event.dy)
}
function dragended(d) {}
And then call .call(drag) on your text labels.
const labels = ['Drag Me1', 'Drag Me2', 'Drag Me3']
d3.select('svg')
.selectAll('text')
.data(labels)
.enter()
.append('text')
.text(d => d)
.attr('fill', 'green')
.attr('x', (d, i) => 10 + i*30)
.attr('y', (d, i) => 15 + i*30)
.call(drag)
Append a rect behind the text, then .call(drag) on your rect. To get a suitable rect, you can use text.getBBox().
In d3 js I follow line chart zoom-able graph link jsfiddle.net/KSAbK/1/ in this line graph I want to shows dot visible like this. how should I do this?
After updating code dot are visible but when I scrolled graph dot was fixed at given positions, below is my zoomed function
function zoomed() {
console.log(d3.event.translate);
console.log(d3.event.scale);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
svg.select(".line")
.attr("class", "line")
.attr("d", line);
svg.selectAll(".dot")
.data(data)
.enter().append("rect")
.attr("class","dot")
.attr("height",7)
.attr("width",7)
.attr("x", function(d) { return x(d.date); })
.attr("y", function(d) { return y(d.value); });
}
you need to add circles where you have data points. The circles could be small, and grow or change its color on mouse over. You can add circles to your chart with
// Append circles to the chart
var circles = chartBody.selectAll('circle')
.data(data)
.attr('cx', function(d) { return x(d.date); })
.attr('cy', function(d) { return y(d.value); })
.attr('r', 10)
.attr('fill', 'blue');
Draw the points after drawing the path, so they are not hidden by the line. Regards,
If you are not doing it with animation, then first remove old circles using
d3.selectAll(".dot").remove();
OR
svg.selectAll(".dot").remove();
And redraw it as per your requirement, so your code will be probably as below.
function zoomed(zoomlevel) {
console.log(d3.event.translate);
console.log(d3.event.scale);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
svg.select(".line")
.attr("class", "line")
.attr("d", line);
svg.selectAll(".dot").remove();
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr('cx', function(d) { return x(d.date); })
.attr('cy', function(d) { return y(d.value); })
.attr('r', zoomlevel*10) //zoomlevel can be -2,-1,1,2... except zero
.attr('fill', 'blue');
}
I have a scatter plot made in D3 with circles denoting each data point. Here's my code:
viz.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr("cx", function(d) {return x(d.x)})
.attr("cy", function(d) {return y(d.y)})
.attr("r", 5)
.attr("fill", function(d) {return d.color})
.on('mouseover', function(d){
console.log(d.color)
})
What I would like to do is, when a given circle is hovered on, connect all circles through a line that have the same color. How can I do this? I can get the color logged into the console, but I don't understand how I can connect all points with the same color through a line upon mouse click?
You can assign a class with color code to your circles. Use d3.selectAll to retrieve all of them on mouseover. Then retrieve their coordinates and pass the coordinates to draw d3.svg.line.
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", function(d) {
return 'dot color-' + color(d.species).replace('#','');
})
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.sepalWidth); })
.attr("cy", function(d) { return y(d.sepalLength); })
.attr("dot-color", function(d) { return color(d.species).replace('#',''); })
.style("fill", function(d) { return color(d.species); })
.on("mouseover", function() {
d3.selectAll(".color-" + $(this).attr("dot-color"))
.attr("r", 5.5);
})
.on("mouseout", function() {
d3.selectAll(".color-" + $(this).attr("dot-color"))
.attr("r", 3.5);
});
Here's an example with color hover:
http://vida.io/documents/KinEKRkSPSfStA4Eu
You can also do it without relying on a common class attribute. In the mouseover handler:
d3.selectAll('.dot')
.filter(function (dOther) { return d.color == dOther.color })
.attr('r', 3.5)