score:2

Accepted answer

Here is a snippet based on your data and sketch. The sizes / offsets / positions are very proprietary, please adjust them according to your design requirements.

var barData = [{
    "Year": "2019",
    "Value": 5388
},
{
    "Year": "2020",
    "Value": 6453
},
{
    "Year": "2021",
    "Value": 7345
}];

const svg = d3.select('svg');

const xScale = d3.scaleBand()
    .domain(barData.map(d => d.Year))
    .range([0, 240]);
       
    
const xAxis = d3.axisBottom(xScale);
  
  svg.append('g')
    .attr('transform', 'translate(50,170)')
    .call(xAxis);

const yScale = d3.scaleLinear()
    .domain([0, 10000])
    .range([150, 0]);
    
const yAxis = d3.axisLeft(yScale).ticks(4);
  
  svg.append('g')
    .attr('transform', `translate(50,20)`)
    .call(yAxis);

const bars = svg.selectAll('g.bar')
  .data(barData)
  .enter()
  .append('g')
  .classed('bar', true)
  .attr('transform', d => `translate(${xScale(d.Year) + 50 + xScale.bandwidth() / 2}, 170)`)
  
  bars.append('rect')
    .attr('x', -20)
    .attr('width', 40)
    .attr('y', d => -150 + yScale(d.Value))
    .attr('height', d => 150 - yScale(d.Value))
    .style('fill', 'blue')
    
 bars.append('text')
    .text(d => d.Value)
  .attr('text-anchor', 'middle')
  .attr('y', d => -155 + yScale(d.Value))
  
bars.filter((d, i) => i < barData.length - 1)
  .append('path')
  .attr('d', (d, i) => `M 5,${-170 + yScale(d.Value)} V ${-210 + yScale(d.Value)} H ${xScale.bandwidth() - 5} V ${-180 + yScale(barData[i + 1].Value)}`)
  .style('stroke', 'gray')
  .style('fill', 'none')
  .attr('marker-end', 'url(#arrowhead)')
  
bars.filter((d, i) => i < barData.length - 1)
  .append('rect')
  .attr('x', 15)
  .attr('y', d => -220 + yScale(d.Value))
  .attr('width', xScale.bandwidth() - 30)
  .attr('height', 20)
  .attr('rx', 10)
  .style('fill', 'white')
  .style('stroke', 'gray');
  
bars.filter((d, i) => i < barData.length - 1)
  .append('text')
  .text((d, i) => `${barData[i + 1].Value > d.Value ? '+' : '-'}${Math.round((barData[i + 1].Value / d.Value * 100) - 100)}%`)
  .attr('x', xScale.bandwidth() / 2)
  .attr('y', d => -207 + yScale(d.Value))
  .attr('text-anchor', 'middle')
  .style('fill', 'black');
  
text {
  font-size: 12px;
  font-family: "Ubuntu";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="300" height="200">
  <defs>
    <marker id="arrowhead" markerWidth="10" markerHeight="7" 
    refX="0" refY="3.5" orient="auto">
      <polygon fill="gray" points="0 0, 10 3.5, 0 7" />
    </marker>
  </defs>
</svg>


Related Query

More Query from same tag