DEV Community

Cover image for How to build a Radial Bar Chart with D3 JS and React JS
simbamkenya
simbamkenya

Posted on • Edited on

How to build a Radial Bar Chart with D3 JS and React JS

Technologies

D3 JS is an amazing JavaScript library for building dynamic and interactive data visualization charts on the web. The library works seamlessly with Scalable Vector Graphic (SVG) to render charts. SVG is a file format based on vector image. It allows developers to define two-dimensional graphics and supports animation and interactivity. SVG can be styled with CSS to create aesthetically pleasing charts.

The project will utilize React js to handle rendering. React is an open-source JavaScript library. It is used to build user interfaces based on components. The library will be crucial in building various UI elements in this chart.

What you will learn

  • Working with d3.arc()
  • Building a dynamic chart
  • Styling SVG chart

How to build a project with D3 JS

This project will entail building a complex chart using D3 JS and React JS. Specifically, we will be building a radial bar chart which is a common chart on the dashboard for visualizing KPI's indicators as a percentage.

radial chart d3.js
In this project, I opted for a slider with a range of 0 to 100. The slider will simulate a dynamic data source. This is an imitation of a sales dashboard taking data from a database and presenting a conversion rate indicator to the end user.
React JS will manage the state of the chart. It will monitor changes on the slider and update the data being fed to the chart. The useState hook will come in handy in managing state.

The radial chart will have the following elements:
1. Two arcs
2. Text labels
3. Slider to stimulate dynamic data

One arc will act as the background the other as the foreground. The background arc will help visualize the remaining part that is not covered by the first arc. It gives an illusion of background color on the path that the main arc of the chart will follow.

Setting up the project

  • Creating react app
npm create vite@latest radialbarchart -- --template react
Enter fullscreen mode Exit fullscreen mode
  • Navigate to the folder with the project
cd radialbarchart
Enter fullscreen mode Exit fullscreen mode
  • Install dependencies (D3 js library in this case)
npm install d3
Enter fullscreen mode Exit fullscreen mode
  • Run the project using the command
npm run dev

Enter fullscreen mode Exit fullscreen mode
  1. Figure out data

D3 charts are data-driven. Visualization of data, make comprehension of trends and patterns from users.
In our case, input values will come from the slider. The slider will simulate value changes based on the provided range.
React Js will keep the chart and slider value in sync. This is achieved by the useState hook to maintain the state.

Managing state

import { useEffect, useState, useRef } from react';

const [sliderValue, setSliderValue] = useState(80)

function handleChange(e){
      setSliderValue(e.target.value)
}

Enter fullscreen mode Exit fullscreen mode

HTML Slider (input data)

  <div>
       <div>{sliderValue}</div>
        <div className="slidecontainer">
          <input 
          type="range" 
          min="0" 
          max="100"  
          value={sliderValue}  
          onChange={handleChange}/>
        </div>
    </div>
Enter fullscreen mode Exit fullscreen mode

Handling re-rendering
The code for drawing the entire chart will be encapsulated in a single function named drawChart. The useEffect hook will help re-render that chart every time the sliderValue changes.

useEffect(() => {
    drawChart(sliderValue)
}, [sliderValue])

function drawChart(value){
  //code for drawing chart
}
Enter fullscreen mode Exit fullscreen mode

Drawing and setting up SVG element

  • The height and width values will be used to set the size of the viewBox attribute on the SVG.
  • Tau represents the whole arc, a complete arc is 360 degrees which is equivalent to 2 * Math.PI
  • maxValue will prove useful when converting values to percentages
function drawChart(value){
     const height = 350, width=960;
     const tau = 2 * Math.PI;
     const maxValue = 100;
     const slice = value/maxValue

   //other code for drawing chart will go here
}
Enter fullscreen mode Exit fullscreen mode

Prepare DOM element
The next step is selecting the element that we will hook our charts elements. React js provides useRef hook to attach the SVG element on the div. Note that we set height and width as 100% and make use of the viewBox property. This ensures that our SVG chart is responsive to different screen sizes.
We attach a g element which is equivalent to a div in HTML to group elements together. This way it is easier to move a group of elements on an SVG element. The g element is transformed to the center of the SVG element by the transform property.

const containerRef = useRef(null)
Enter fullscreen mode Exit fullscreen mode
  <div className='container'>
    <div ref={containerRef}></div>
  <di>
Enter fullscreen mode Exit fullscreen mode
select("svg").remove() 
const svg = select(containerRef.current)
           .append('svg')
           .attr('height', '60%')
           .attr('width', '100%')
           .attr("viewBox", `0 0 ${width} ${height}`)
           .append("g")
           .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")

Enter fullscreen mode Exit fullscreen mode

Before drawing the SVG chart, it is essential to remove any SVG element from the previous render. This is achieved by remove() function.

Drawing arcs

An arc is a curve, but in mathematics and computing an arc is a section of a circle. D3 provides utility functions to draw arcs commonly known as arc generators.
In this case, we have an arc generator called arcGen. It will handle the creation of arcs. The arcGen generates an SVG path based on the provided data value.
D3 library helps a developer make complex shapes through shape generators. The d3.arc() returns a function that generates arcs based on provided end angle. It takes input value from the slider and updates the chart.

const  innerRadius= 90, outerRadius= 120, 
startAngle= 0, cornerRadius= 40;

const arcGen = arc()
    .innerRadius(innerRadius)
    .outerRadius(outerRadius)
    .startAngle(startAngle)
    .cornerRadius(cornerRadius)
Enter fullscreen mode Exit fullscreen mode

This radial chart has two arcs namely arc1 and foreground.

   const arc1 = svg.append("path")
          .datum({endAngle: tau})
          .style("fill", "#ddd")
          .attr("d", arcGen);

   const foreground = svg.append("path")
          .datum({endAngle: slice * tau})
          .attr('fill', '#F57B21')
          .attr("d", arcGen);
Enter fullscreen mode Exit fullscreen mode
  1. The arc1 arc has a very light shade of gray and takes an object endAngle of 2*Math.PI.
  2. The foreground arc is proportional to the input value of sales. Providing a value of 100% from the slider will be a donut chart. The foreground arc will have an orange color which is easily visible to users.

Adding text labels on the chart

Labels make charts easier to understand.

  svg.append("text")
          .attr("text-anchor", "middle")
          .text(`${value}%`)
          .style('font-size', '3.2em')
          .style('fill', '#A9BF51');
Enter fullscreen mode Exit fullscreen mode

The text value is attached to the SVG element and styled using CSS. The text value also pumped up to have font-size of 3.2em and a color value of '#A9BF51'.
CSS style text-anchor with the value of middle will center text value horizontally. The chart will have text in the arc to show the value visualized by the arc.

   svg.append("text")
      .attr("text-anchor", "middle")
      .text("Sales")
      .attr('dy', '1.45em')
      .style('font-size', '1.75em');
Enter fullscreen mode Exit fullscreen mode

The label value is moved horizontally using dy attribute by 1.45em and gets a smaller font-sizeof 1.75em.

Final Code

Code: CODE
Live chart: LIVE CHART

Here is the final code:

import { select, arc} from 'd3'
import './App.css';
import { useEffect, useState, useRef } from 'react';

function App() {
  const [sliderValue, setSliderValue] = useState(80)
  const containerRef = useRef(null)

  useEffect(() => {
    drawChart(sliderValue)
  }, [sliderValue])

    function handleChange(e){
      setSliderValue(e.target.value)
    }

    function drawChart(value){
      const height = 350, width=960;
      const tau = 2 * Math.PI;
      const maxValue = 100;
      const slice = value/maxValue
      const  innerRadius= 90, outerRadius= 120, startAngle= 0, cornerRadius= 40;

      select("svg").remove()

      const svg = select(containerRef.current)
                  .append('svg')
                  .attr('height', '60%')
                  .attr('width', '100%')
                  .attr("viewBox", `0 0 ${width} ${height}`)
                  .append("g")
                  .attr("transform", "translate(" + width / 2
                  + "," + height / 2 + ")")

        // An arc will be created
      const arcGen = arc()
          .innerRadius(innerRadius)
          .outerRadius(outerRadius)
          .startAngle(startAngle)
          .cornerRadius(cornerRadius)

      const arc1 = svg.append("path")
          .datum({endAngle: tau})
          .style("fill", "#ddd")
          .attr("d", arcGen);


      const foreground = svg.append("path")
          .datum({endAngle: slice * tau})
          .attr('fill', '#F57B21')
          .attr("d", arcGen);

      svg.append("text")
          .attr("text-anchor", "middle")
          .text(`${value}%`)
          .style('font-size', '3.2em')
          .style('fill', '#A9BF51');

      svg.append("text")
          .attr("text-anchor", "middle")
          .text("Sales")
          .attr('dy', '1.45em')
          .style('font-size', '1.75em');
      }


  return (
    <div className='container'>
        <div ref={containerRef}></div>
        <div className="slidecontainer"> 
          <div className='salesfigure'>{sliderValue}</div>
          <input 
            type="range" 
            min="0"
            max="100" 
            value={sliderValue} 
            onChange={handleChange}
          />
        </div>
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Conclusion

A radial chart is an interesting and useful chart. D3 js utilized d3.arc() to create the charts. This is a complex chart that is broken down into three parts, foreground, background, and slider.

I am open to freelancing work on D3 and data visualization. Contact me on X simbamkenya

Thank you for your time

Top comments (2)

Collapse
 
brianbravoski profile image
Brian Kemboi

This is a really well put article. Thanks for sharing your expertise in d3js and looking forward to implement such in various projects. 👌

Collapse
 
simbamkenya profile image
simbamkenya

I really love D3 JS, I will keep them coming.