I have a tall web page filled with visualizations created with D3. I set up a transition that makes the bars in a bar chart “fall down” from the top of the plot over the course of 2 seconds.
I would like to trigger this transition()
to happen only when the user scrolls this element into view (otherwise, it happens immediately when the page loads and the user doesn’t see the floating effect because it will take he/she a few minutes to scroll down to this element).
The parent element is named bar-chart-div
.
Here is my code:
let data = [
{"color": "Blue", "plotValue": 11},
{"color": "Red", "plotValue": 20},
{"color": "Orange", "plotValue": 21},
{"color": "Purple", "plotValue": 30},
{"color": "Green", "plotValue": 35},
{"color": "Violet", "plotValue": 40},
vis.margin = {top: 70, right: 90, bottom: 90, left: 80};
vis.width = 800 - vis.margin.left - vis.margin.right;
vis.height = 700 - vis.margin.top - vis.margin.bottom;
// initialize the drawing area
vis.svg = d3.select('#' + vis.parentElement).append('svg')
.attr('width', vis.width + vis.margin.left + vis.margin.right)
.attr('height', vis.height + vis.margin.top + vis.margin.bottom)
.append('g')
.attr('transform', `translate (${vis.margin.left}, ${vis.margin.top})`);
// scales and axes
vis.xScale = d3.scaleBand()
.range( [ 0, vis.width ] )
.padding(0.4);
vis.yScale = d3.scaleLinear()
.range( [ vis.height, 0 ] );
vis.xAxis = d3.axisBottom()
.scale(vis.xScale);
vis.yAxis = d3.axisLeft()
.scale(vis.yScale);
// add chart title
vis.svg.append('g')
.attr('class', 'title bar-chart-title')
.append('text')
.text(vis.chartTitle)
.attr('transform', `translate(${vis.width / 2 + 45}, -50)`)
.attr('text-anchor', 'middle');
// tooltip
vis.tooltip = d3.select('body').append('div')
.attr('class', 'tooltip')
.attr('id', 'barChartTooltip');
// create the axis groups
vis.xAxisGroup = vis.svg.append('g')
.attr('class', 'x-axis axis')
.attr('transform', 'translate(0, ' + vis.height + ')');
vis.yAxisGroup = vis.svg.append('g')
.attr('class', 'y-axis axis');
vis.xScale.domain(vis.data.map(function (d) { return d.color; }));
vis.yScale.domain( [ 100, 0 ] );
// draw the bars
vis.bars = vis.svg.selectAll('.bar')
.data(vis.data)
vis.bars.exit().remove();
vis.bars
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', d => vis.xScale(d.color))
// .attr('y', d => vis.yScale(vis.height))
// .attr('y', d => vis.yScale(0))
.attr('y', d => d)
.attr('width', vis.xScale.bandwidth())
.on('mouseover', function(event, d) {
d3.select(this)
.attr('stroke-width', '2px')
.attr('stroke', 'grey')
.attr('fill', 'blue')
vis.tooltip
.style('opacity', 1)
.style('left', event.pageX + 20 + 'px')
.style('top', event.pageY + 'px')
.html(`
<div style='border: thin solid grey; border-radius: 5px; background: lightgrey; padding: 20px'>
<h3>${d.color}</h3>
<h4> ${100- d.plotValue.toLocaleString()}% decline </h4>
</div>
`)
})
.on('mouseout', function(event, d) {
d3.select(this)
.attr('stroke-width', 1)
.attr('stroke', 'blue')
.attr('fill', function(d) {
return 'red'
})
vis.tooltip
.style('opacity', 0)
.style('left', 0)
.style('top', 0)
.html(``);
})
.merge(vis.bars)
.transition()
.duration(2000)
.attr('y', d => vis.yScale(d.plotValue) )
.attr('height', function(d) { return vis.height - vis.yScale(d.plotValue); })
.attr('fill', 'red')
// add the axes
vis.xAxisGroup
.transition()
.duration(500)
.style('font-size', '15px')
.style('color', 'blue')
.call(d3.axisBottom((vis.xScale)))
.selectAll('text')
.attr('y', 30)
.attr('x', -35)
.attr('dy', '.35em')
.attr('transform', 'rotate(-30)')
;
vis.yAxisGroup
.transition()
.duration(500)
.style('font-size', '15px')
.style('color', 'blue')
.call(d3.axisLeft(vis.yScale));
vis.text = vis.svg.selectAll('.text')
.data(vis.data)
vis.text
.enter()
.append('text')
.attr('class', 'text')
.attr('text-anchor', 'middle')
.attr('x', d => vis.xScale(d.color) + 27)
.attr('y', d => vis.yScale(100- d.plotValue) + 20)
.text( function (d) {
return '-' + (100 - d.plotValue) + '%';
})
.style('font-weight', 600)
.style('fill', 'black')
Thanks in advance for your help!