Reputation: 515
I have been working on a horizontal bar chart. I added transition
to the width
attr of the rect
s from 0 to the desired value and they worked fine. Then I added some circle
elements and made them "appear" one after another using delay
, tweening the r
attribute from 0 to the desired r
value as well, and everything went fine too. The visualization works as expected if I don't hover over any element before all of them have finished their transitions.
Both the rect
s and circle
s are tweening their width
and r
attributes at different delays, which makes some of them to be shown before others.
The problem: if I hover over any rect
while the other elements have not finished their transitions, all of them suddenly stop transitioning their attributes. Hence, the desired final state of the whole chart is not reached, actually it becomes a mess. I can't figure out why hovering over an element can interfere with the expected behavior of other "apparently" independent elements.
function draw(){
var width = $( window ).width() ;
var height = document.body.clientHeight ;
var data = [
{country:"Pichonita", growth: 15},
{country:"Andromeda", growth: 12},
{country:"India", growth: 33},
{country:"Indonesia", growth: 22},
{country:"Russia", growth: 6},
{country:"Mars", growth: 41},
{country:"Pluton", growth: 16},
{country:"Earth", growth: 24},
{country:"Neptune", growth: 8}
];
//set margins
var margin = {top:30, right:30, bottom:30, left:40};
var width = width - margin.left - margin.right*2.5;
var height = height - margin.top - margin.bottom;
//set scales & ranges
var xScale = d3.scaleLinear()
.range([0, width - 100])
var yScale = d3.scaleBand()
.range([0, height]).padding(.2)
//draw the svg
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right * 3)
.attr("height",height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left*2 + "," + margin.top + ")")
//force data
data.forEach(function(d){
return d.growth = +d.growth;
});
//set domains
yScale.domain(data.map(d => d.country))
xScale.domain([0, d3.max(data, d=> d.growth)])
//add X & Y axes and append the bars to Y axis
var xAxis = svg.append("g")
.attr("class",xAxis)
.attr("transform","translate(" + 0 + "," + height + ")")
.call(d3.axisBottom(xScale))
var yAxis = svg.append("g")
.attr("class",yAxis)
.call(d3.axisLeft(yScale))
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("stroke","transparent")
.attr("stroke-width",4)
.on("mouseover", function(){d3.select(this).transition().duration(600).attr("stroke","#6D2077").attr("stroke-width",3).style("fill","#6D2077")
d3.selectAll(".textCircle").transition().duration(600)
.attr("r",yScale.bandwidth() / 1.9)
.attr("stroke","#6D2077")
.attr("stroke-width",1)
})
.on("mouseout", function(){d3.select(this)
.transition()
.duration(600)
.attr("stroke","transparent")
.attr("stroke-width",0)
.style("fill","#00338D")
d3.selectAll(".textCircle")
.transition().duration(600)
.attr("r", yScale.bandwidth() / 2)
.attr("stroke","transparent")
})
.attr("class","bar")
.attr("height",yScale.bandwidth())
.attr("x",0.5)
.attr("y",function(d){
return yScale(d.country)
})
.attr("width",0)
.transition()
.duration(3800)
.delay( (d,i)=> (i+1) *200)
.ease(d3.easeElastic)
.attr("width", function(d){
return xScale(d.growth)
})
.style("fill","#00338D")
var newG = svg.append("g")
newG.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","textCircle")
.attr("cx",d=> xScale(d.growth) )
.attr("cy",d=> yScale(d.country) + yScale.bandwidth() / 2)
.attr("r",0)
.transition()
.duration(1200)
.delay( (d,i)=> (i+1) *450)
.attr("r",yScale.bandwidth() / 2)
.attr("opacity",1)
.style("fill","#0091DA")
.attr("stroke","transparent")
}
draw();
$( window ).resize(function() {
$("body").empty();
draw();
});
html{
height: 98%;
margin: 0;
padding: 0;
}
body{
min-height: 98%;
margin: 0;
padding: 0;
}
svg{
text-rendering: geometricPrecision;
shape-rendering:geometricPrecision;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Upvotes: 1
Views: 416
Reputation: 9520
Things are getting messed up because you've added mouseover
and mouseout
event listeners that try to perform actions that conflict with the ongoing transition events. To fix the problem, don't add the mouseover
/mouseout
listeners until after the chart bars have performed their initial transition. You can add a listener for the end of the transition using transition.on('end', function(){...}
and then add the mouse event listeners to the DOM elements once the transition has completed.
d3.select('#whateverItIs')
// stuff to do prior to transition
.transition()
// transition stuff
.on('end', function() {
d3.select(this)
.on("mouseover", function() {
// handler code here
})
.on("mouseout", function() {
// handler code here
})
})
With your code:
function draw() {
var width = $(window).width();
var height = document.body.clientHeight;
var data = [{
country: "Pichonita",
growth: 15
},
{
country: "Andromeda",
growth: 12
},
{
country: "India",
growth: 33
},
{
country: "Indonesia",
growth: 22
},
{
country: "Russia",
growth: 6
},
{
country: "Mars",
growth: 41
},
{
country: "Pluton",
growth: 16
},
{
country: "Earth",
growth: 24
},
{
country: "Neptune",
growth: 8
}
];
//set margins
var margin = {
top: 30,
right: 30,
bottom: 30,
left: 40
};
var width = width - margin.left - margin.right * 2.5;
var height = height - margin.top - margin.bottom;
//set scales & ranges
var xScale = d3.scaleLinear()
.range([0, width - 100])
var yScale = d3.scaleBand()
.range([0, height]).padding(.2)
//draw the svg
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right * 3)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left * 2 + "," + margin.top + ")")
//force data
data.forEach(function(d) {
return d.growth = +d.growth;
});
//set domains
yScale.domain(data.map(d => d.country))
xScale.domain([0, d3.max(data, d => d.growth)])
//add X & Y axes and append the bars to Y axis
var xAxis = svg.append("g")
.attr("class", xAxis)
.attr("transform", "translate(" + 0 + "," + height + ")")
.call(d3.axisBottom(xScale))
var yAxis = svg.append("g")
.attr("class", yAxis)
.call(d3.axisLeft(yScale))
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("stroke", "transparent")
.attr("stroke-width", 4)
.attr("class", "bar")
.attr("height", yScale.bandwidth())
.attr("x", 0.5)
.attr("y", function(d) {
return yScale(d.country)
})
.attr("width", 0)
.transition()
.duration(3800)
.delay((d, i) => (i + 1) * 200)
.ease(d3.easeElastic)
.attr("width", function(d) {
return xScale(d.growth)
})
.style("fill", "#00338D")
.on('end', function() {
d3.select(this)
.on("mouseover", function() {
d3.select(this)
.transition().duration(600)
.attr("stroke", "#6D2077")
.attr("stroke-width", 3)
.style("fill", "#6D2077")
d3.selectAll(".textCircle")
.transition().duration(600)
.attr("r", yScale.bandwidth() / 1.9)
.attr("stroke", "#6D2077")
.attr("stroke-width", 1)
})
.on("mouseout", function() {
d3.select(this)
.transition()
.duration(600)
.attr("stroke", "transparent")
.attr("stroke-width", 0)
.style("fill", "#00338D")
d3.selectAll(".textCircle")
.transition().duration(600)
.attr("r", yScale.bandwidth() / 2)
.attr("stroke", "transparent")
})
})
var newG = svg.append("g")
newG.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "textCircle")
.attr("cx", d => xScale(d.growth))
.attr("cy", d => yScale(d.country) + yScale.bandwidth() / 2)
.attr("r", 0)
.transition()
.duration(1200)
.delay((d, i) => (i + 1) * 450)
.attr("r", yScale.bandwidth() / 2)
.attr("opacity", 1)
.style("fill", "#0091DA")
.attr("stroke", "transparent")
}
draw();
$(window).resize(function() {
$("body").empty();
draw();
});
html{
height: 98%;
margin: 0;
padding: 0;
}
body{
min-height: 98%;
margin: 0;
padding: 0;
}
svg{
text-rendering: geometricPrecision;
shape-rendering:geometricPrecision;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Upvotes: 4