Iterating over a data array
In this tutorial, we will be iterating over data using svelte #each
blocks. We don’t need D3.js at this stage.
Circles
Let’s use svg circle
element to place a few circles on our svg.
<script>
let data = [
{ x: 180, y: 30, r: 5, fill: 'orange' },
{ x: 100, y: 40, r: 25, fill: 'red' },
{ x: 170, y: 160, r: 50, fill: 'cyan' },
{ x: 120, y: 220, r: 2, fill: 'yellow' },
{ x: 30, y: 100, r: 10, fill: 'fuchsia' }
];
// I have added a setTimeout function which will update the data
setTimeout(() => {
data = [
{ x: 40, y: 70, r: 5, fill: 'red' },
{ x: 100, y: 210, r: 25, fill: 'orange' },
{ x: 120, y: 100, r: 50, fill: 'blue' },
{ x: 350, y: 220, r: 2, fill: 'red' },
{ x: 230, y: 190, r: 10, fill: 'white' }
];
}, 2000);
</script>
<div>
<svg viewBox="0 0 600 300">
{#each data as d}
<circle cx={d.x} cy={d.y} r={d.r} fill={d.fill} />
{/each}
</svg>
</div>
<style>
/* The circles will transition to their new state using css. */
circle {
transition: all 1s ease;
}
</style>
Rects
Similarly, we can use svg <rect>
elements to create bars.
<script>
let data = [
{ x: 10, y: 10, width: 5, height: 40 },
{ x: 10, y: 60, width: 5, height: 40 },
{ x: 10, y: 110, width: 5, height: 40 },
{ x: 10, y: 160, width: 5, height: 40 },
{ x: 10, y: 210, width: 5, height: 40 }
];
setTimeout(() => {
data = [
{ x: 10, y: 10, width: 450, height: 40 },
{ x: 10, y: 60, width: 320, height: 40 },
{ x: 10, y: 110, width: 160, height: 40 },
{ x: 10, y: 160, width: 80, height: 40 },
{ x: 10, y: 210, width: 10, height: 40 }
];
}, 4000);
</script>
<div>
<svg viewBox="0 0 600 300">
{#each data as d}
<rect x={d.x} y={d.y} width={d.width} height={d.height} fill="white" />
{/each}
</svg>
</div>
<style>
rect {
transition: all 1s ease;
}
</style>
Text
Let’s add text labels to the bars we just created. In svg, texts are added by using the <text>
element.
Additionally, we will use here tweened
function from svelte/motion for creating a writable store whose values change over time rather than immediately so we can animate the bar labels to their final values.
<script>
import { tweened } from 'svelte/motion';
let data = [
{ x: 10, y: 10, width: 5, height: 40 },
{ x: 10, y: 60, width: 5, height: 40 },
{ x: 10, y: 110, width: 5, height: 40 },
{ x: 10, y: 160, width: 5, height: 40 },
{ x: 10, y: 210, width: 5, height: 40 }
];
const position = (x, y) => `transform: translate(${x}px, ${y}px)`;
const textTween = tweened(
data.map((d) => d.width),
{ duration: 1000 }
);
// This will run when the data changes i.e after handleClick runs.
$: textTween.set(data.map((d) => d.width));
const handleClick = () => {
data = [
{ x: 10, y: 10, width: 450 * Math.random(), height: 40 },
{ x: 10, y: 60, width: 320 * Math.random(), height: 40 },
{ x: 10, y: 110, width: 160 * Math.random(), height: 40 },
{ x: 10, y: 160, width: 80 * Math.random(), height: 40 },
{ x: 10, y: 210, width: 10 * Math.random(), height: 40 }
];
};
</script>
<div>
<svg viewBox="0 0 600 270">
{#each data as d, i}
<rect x={d.x} y={d.y} width={d.width} height={d.height} fill="white" />
<text
style={position(d.x + d.width + 10, d.y + d.height / 2 + 5)}
fill="white"
text-anchor="start"
>{$textTween[i].toFixed(0)}
</text>
{/each}
</svg>
<button
on:click={handleClick}
class="m-2 rounded-lg bg-yellow-400 px-2 py-2 text-lg font-semibold text-gray-900 hover:bg-yellow-500"
>Animate</button>
</div>
<style>
rect {
transition: all 1s ease;
}
text {
transition: transform 1s ease;
}
</style>
In svg, text elements has x
and y
attributes but they are svg attributes and not css attributes so we can not transition them via css, but we can animate the transform
attribute. Thus we are not setting directly x and y attributes for the text but we will use the transform function e.g. (transform=translate(x,y)) to position the text elements.
Clicking the animate
button will update our data and our text and bars will transition to their new state and the bar labels will be tweened to their new values.
Here text-anchor
property shows where should the starting point of the text should be aligned with. It can be set as start
,middle
or end
.