Example: Chaining Animations

In this example, an animation is triggered after the successful completion of another animation. If the animation is canceled by the start of a different animation, the second animation will not run.

Click the button below to start pulsating the circles from a random position. Click the button again to stop the current sequence and restart from a different random position.

import('https://cdn.jsdelivr.net/npm/counterpoint-vis@latest/dist/counterpoint-vis.es.js').then((counterpoint) => {

  // canvas setup
  const canvas = document.getElementById("dot-canvas");
  canvas.width = canvas.offsetWidth * window.devicePixelRatio;
  canvas.height = canvas.offsetHeight * window.devicePixelRatio;

  const baseRadius = 25;
  const pulseRadius = 15;

  // initialize the render group with 100 marks
  let markSet = new counterpoint.MarkRenderGroup(
    new Array(100).fill(0).map(
      (_, i) =>
        new counterpoint.Mark(i, {
          x: (i % 10) * baseRadius * 2 + baseRadius,
          y: Math.floor(i / 10) * baseRadius * 2 + baseRadius,
          color: `hsl(${i / 100 * 360}, 60%, 70%)`,
          radius: baseRadius
        })
    )
  );

  let pulseIndex = 0;
  // Animate button event handler
  document.getElementById('animate-button').addEventListener('click', () => {
    markSet.get(pulseIndex).animateTo('radius', baseRadius, { duration: 500 });
    pulseIndex = Math.floor(Math.random() * 100);
    pulse();
  });

  document.getElementById('stop-button').addEventListener('click', () => {
    markSet.animateTo('radius', baseRadius, { duration: 500 });
  });

  async function pulse() {
    // calling wait() on an attribute returns a Promise that resolves when the
    // animations on that attribute finish, and rejects when any animation on
    // that attribute is canceled
    try {
      await markSet.get(pulseIndex)
      .animateTo('radius', pulseRadius, { duration: 200 })
      .wait('radius');
      markSet.get(pulseIndex).animateTo('radius', baseRadius, { duration: 600 });
      pulseIndex = (pulseIndex + 1) % 100;
      pulse();
    } catch (e) {}
  }

  // drawing function that will get called by the Ticker every time a redraw is needed
  function draw() {
    if (!!canvas) {
      let ctx = canvas.getContext('2d');

      if (!!ctx) {
        ctx.resetTransform();
        ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
        ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
        ctx.strokeStyle = 'white';
        ctx.lineWidth = 1.0;
        markSet.stage.forEach((mark) => {
          let x = mark.attr('x');
          let y = mark.attr('y');
          let color = mark.attr('color');
          let radius = mark.attr('radius');
          ctx.fillStyle = color;
          ctx?.beginPath();
          ctx?.ellipse(x, y, radius, radius, 0, 0, 2 * Math.PI, false);
          ctx?.fill();
          ctx?.stroke();
          ctx?.closePath();
        });
      }
    }
  }

  let ticker = new counterpoint.Ticker(markSet).onChange(draw);
  draw();
});

results matching ""

    No results matching ""