When the data being rendered by a chart changes, sometimes it necessitates a change to the scales and axes of the chart as well. This lesson demonstrates how to animate and synchronize axis transitions on a column chart.
var svg = d3.select('.chart') .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .call(responsivefy) .append('g') .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');/** * Y axis */const yScale = d3.scaleLinear() .domain([ 0, 100 ]) .range([height, 0]);const yAxis = svg .append('g') .call(d3.axisLeft(yScale));/** * x axis */const xScale = d3.scaleBand() .domain(data.map(d => d.name)) .range([0, width]) .padding(0.2);const xAxis = d3.axisBottom(xScale).tickSize(10).tickPadding(5);svg.append('g') .attr('transform', `translate(0, ${height})`) .call(xAxis) .selectAll('text') .attr('text-anchor', 'end') .attr('transform', 'rotate(-45)');// enter: which in the data, but not yet on the page// upate: which in the data, and also in the page// exit: which not in the data, but exist on the page// endfunction render(subject = 'math') { // Define a resuable transation variable var t = d3.transition().duration(1000); var update = svg.selectAll('rect') .data(data.filter(d => d[subject]), d => d.name); //d => d.name is a uniq idientifier // First: we want to remove the existing object which doesn't have any value // Get rid of null value object // Also animation y and height attr to produce // fade out effect update .exit() .transition(t) .attr('y', height) .attr('height', 0) .remove(); // Update the y axis with animation yScale.domain( [0, d3.max(data, d => d[subject])] ); yAxis .transition(t) .delay(1000) .call(d3.axisLeft(yScale)); // Second, we want to animate the existing elements height update .transition(t) .delay(1000) .attr('y', d => yScale(d[subject])) .attr('height', d => height - yScale(d[subject])); // Last, for the new data which is not in the page before // We want to animate update .enter() .append('rect') .attr('y', height) .attr('height', 0) .attr('x', d => xScale(d.name)) .attr('width', d => xScale.bandwidth()) .transition(t) .delay(update.exit().size() ? 2000 : 0) // If page refresh, we don't want to wait 2000ms .attr('y', d => yScale(d[subject])) .attr('height', d => height - yScale(d[subject]));}render();