Very recently, I was looking to add a neat sparkline to a Vue.js application of my own.
As always, I googled just that, looking for sparkline vue.js or sparkline npm. But I couldn't find something that was easy, with a small footprint and yet customizable.
After playing a bit with Chart.js, I just stopped and considered how I could build a decent, yet very simple, sparkline component (i.e. without any dependency).
If you look at how npm's sparkline works as well as the ones from Stripe's dashboard, you will quickly realize that it's just a SVG element that you customize with JavaScript.
So bear with me, I'll show you how to do just that.
Demo
Behind the scene
The sparkline is just a Vue.js component where you provide the coordinates of the sparkline as an array. Here's how I've rendered the sparkline in the example above:
<sparkline v-bind:data="[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]"></sparkline>
The source code of the component is the following:
<template> | |
<svg class="sparkline" :width="width" :height="height" :stroke-width="stroke"> | |
<path class="sparkline--line" :d="shape" fill="none"></path> | |
<path | |
class="sparkline--fill" | |
:d="[shape, fillEndPath].join(' ')" | |
stroke="none" | |
></path> | |
</svg> | |
</template> | |
<script> | |
export default { | |
props: ["data"], | |
data() { | |
return { | |
stroke: 3, | |
width: 256, | |
height: 80, | |
}; | |
}, | |
computed: { | |
shape() { | |
const stroke = this.stroke; | |
const width = this.width; | |
const height = this.height - stroke * 2; | |
const data = this.data || []; | |
const highestPoint = Math.max.apply(null, data) + 1; | |
const coordinates = []; | |
const totalPoints = this.data.length - 1; | |
data.forEach((item, n) => { | |
const x = (n / totalPoints) * width + stroke; | |
const y = height - (item / highestPoint) * height + stroke; | |
coordinates.push({ x, y }); | |
}); | |
if (!coordinates[0]) { | |
return ( | |
"M 0 " + | |
this.stroke + | |
" L 0 " + | |
this.stroke + | |
" L " + | |
this.width + | |
" " + | |
this.stroke | |
); | |
} | |
const path = []; | |
coordinates.forEach((point) => | |
path.push(["L", point.x, point.y].join(" ")) | |
); | |
return ["M" + coordinates[0].x, coordinates[0].y, ...path].join(" "); | |
}, | |
fillEndPath() { | |
return `V ${this.height} L 4 ${this.height} Z`; | |
}, | |
}, | |
}; | |
</script> | |
<style> | |
svg { | |
stroke: #1f8ceb; | |
fill: rgba(31, 140, 235, 0.06); | |
transition: all 1s ease-in-out; | |
} | |
svg path { | |
box-sizing: border-box; | |
} | |
</style> |
As you might have noticed, the code renders an <svg>
HTML element by computing the coordinates of the different <path>
.
There are two <path>
. One for the blue line. And another one for the blue background. I've used the color #1f8ceb
but of course this is totally customizable, just like the width/height of the sparkline.
That component is pretty basic and contrary to NPM or Stripe, it doesn't handle when a mouse hovers the <svg>
. I didn't need that for my use case, but if ever you implement it, feel free to edit the gist and share it with the community.
Top comments (2)
Clever use of Vue!
As always, feel free to share your thoughts/improvements in the comment 🤗