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.

55555w

<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.