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.
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
- Navigate to the folder with the project
cd radialbarchart
- Install dependencies (D3 js library in this case)
npm install d3
- Run the project using the command
npm run dev
- 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)
}
HTML Slider (input data)
<div>
<div>{sliderValue}</div>
<div className="slidecontainer">
<input
type="range"
min="0"
max="100"
value={sliderValue}
onChange={handleChange}/>
</div>
</div>
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
}
Drawing and setting up SVG element
- The
height
andwidth
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 to2 * 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
}
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)
<div className='container'>
<div ref={containerRef}></div>
<di>
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 + ")")
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)
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);
- The
arc1
arc has a very light shade of gray and takes an objectendAngle
of2*Math.PI
. - 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');
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');
The label value is moved horizontally using dy
attribute by 1.45em
and gets a smaller font-size
of 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;
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)
This is a really well put article. Thanks for sharing your expertise in d3js and looking forward to implement such in various projects. 👌
I really love D3 JS, I will keep them coming.