Create a Bar Chart
Let’s create the simple bar chart as below using D3 scaleLinear and Svelte. What does a scale function do is basically mapping the data values to the space available in our chart area.
Chart preparation steps
- Fetch/import the data to be graphed.
- Set dimensions, margins & scales.
- Create functions needed to create the line/path elements or helper functions, e.g d3.line d3.arc when needed.
- Create main data elements (usually by iteration, using svelte each
blocks).
Bar Chart
<script>
import { scaleLinear } from 'd3-scale';
// 1. Basic Setup: Get the data
// Some random birthrate data
const points = [
{ year: 1990, birthrate: 6.7 },
{ year: 1995, birthrate: 4.6 },
{ year: 2000, birthrate: 14.4 },
{ year: 2005, birthrate: 18 },
{ year: 2010, birthrate: 7 },
{ year: 2015, birthrate: 12.4 },
{ year: 2020, birthrate: 17 },
{ year: 2025, birthrate: 10.9 },
{ year: 2030, birthrate: 8 },
{ year: 2035, birthrate: 12.9 }
];
// 2. Dimensions, Margins & Scales
// Data for plotting x-y axis
const yTicks = [0, 5, 10, 15, 20];
const padding = { top: 20, right: 15, bottom: 20, left: 25 };
let width = 500;
let height = 350;
$: xScale = scaleLinear()
.domain([0, points.length])
.range([padding.left, width - padding.right]);
let yScale = scaleLinear()
.domain([0, Math.max.apply(null, yTicks)])
.range([height - padding.bottom, padding.top]);
$: innerWidth = width - (padding.left + padding.right);
$: barWidth = innerWidth / points.length;
// 3. Functions needed to create the Data elements or Helper functions, e.g d3.line d3.arc when needed
// Shorten the date axis values for mobile
function formatMobile(tick) {
return "'" + tick.toString().slice(-2);
}
// 4. Create the main data elements (usually by iteration, using svelte each blocks)
// We will do this in the html markup
</script>
<div class="chart" bind:clientWidth={width}>
<svg {width} {height}>
<!-- 4. Design the bars -->
<g class="bars">
{#each points as point, i}
<rect
x={xScale(i) + 2}
y={yScale(point.birthrate)}
width={barWidth * 0.9}
height={yScale(0) - yScale(point.birthrate)} />
<!-- Circle showing the start of each Bar -->
<circle
cx={xScale(i) + 2}
cy={yScale(point.birthrate)}
fill="white"
r="5" />
{/each}
</g>
<!-- Design y axis -->
<g class="axis y-axis">
{#each yTicks as tick}
<g class="tick tick-{tick}" transform="translate(0, {yScale(tick)})">
<line x2="100%" />
<text y="-4"
>{tick} {tick === 20 ? ' per 1,000 population' : ''}</text>
</g>
{/each}
</g>
<!-- Design x axis -->
<g class="axis x-axis">
{#each points as point, i}
<g class="tick" transform="translate({xScale(i)}, {height})">
<text x={barWidth / 2} y="-4">
{width > 380 ? point.year : formatMobile(point.year)}</text>
</g>
{/each}
</g>
</svg>
</div>
<style>
.x-axis .tick text {
text-anchor: middle;
color: white;
}
.bars rect {
fill: #fcd34d;
stroke: none;
}
.tick {
font-family: Poppins, sans-serif;
font-size: 0.725em;
font-weight: 200;
color: white;
}
.tick text {
fill: white;
text-anchor: start;
color: white;
}
.tick line {
stroke: #fcd34d;
stroke-dasharray: 2;
opacity: 1;
}
.tick.tick-0 line {
display: inline-block;
stroke-dasharray: 0;
}
</style>
Differently to a classical x-y chart the origin in an svg element (0,0) is the top left corner. This is important to know when drawing shapes on an svg. I added a circle point to show you the starting point of each bar element. The <rect>
elements is drawn starting from top left corner at the point(x,y)
determined by the x, y attributes and drawn to the right with the width
and down with the height
attributes.
This will be important when we will animate the height to the bars.