Search code examples
javascriptanimationcanvaspathkonva

Konva animation iteration counter to stop animation after 1


I wanted to animate the yellowLine to flow one time from left to tight across the screen. After that it should stay solid. The added animation works as it should but the yellowLine gets shortened. Is there a different approach to the anim.stop(), like an iteration counter?

<div class="content-container">
  <div id="stage">
    <div style="position: relative; user-select: none; width: 1425px; height: 392.5px;" class="konvajs-content" role="presentation"><canvas style="padding: 0px; margin: 0px; border: 0px none; background: transparent; position: absolute; top: 0px; left: 0px; width: 1425px; height: 392.5px; display: block;" width="1425" height="392"></canvas></div>
  </div>
</div>
<script src="https://unpkg.com/konva@9/konva.min.js"></script>
const stage = new Konva.Stage({
  width: window.innerWidth,
  height: document.querySelector('.content-container').clientHeight / 2,
  container: 'stage',
});

const layer = new Konva.Layer();

const bluePoly = new Konva.Line({
  points: [0, 0, stage.width(), 0, stage.width() / 2, stage.height() / 2, 0, stage.height() / 2],
  fill: '#3769cd',
  closed: true,
});

const whitePoly = new Konva.Line({
  points: [0, 0, stage.width() / 2, 0, (stage.width() / 2) * 1.5, stage.height() / 2, 0, stage.height() / 2],
  fill: '#ffffff',
  closed: true,
  x: 0,
  y: stage.height() / 2,
});

const yellowLine = new Konva.Line({
  points: [0, 0, stage.width() / 2, 0, ((stage.width() / 2) * 1.5) - 50, (stage.height() / 2) - 20, stage.width(), (stage.height() / 2) - 20],
  stroke: 'yellow',
  strokeWidth: 10,
  x: 0,
  y: stage.height() / 2,
  dash: [stage.width(), stage.width()],
  dashOffset: stage.width(),
});

const anim = new Konva.Animation(function(frame) {
  yellowLine.dashOffset(stage.width() - (frame.time * stage.width() / 2055));
}, layer);

anim.start();

setTimeout(function() {
  anim.stop();
  redrawYellowLine();
}, 2055);

const redrawYellowLine = () => {
  const viewportWidth = stage.width();
  const viewportHeight = stage.height();

  yellowLine.points([0, 0, viewportWidth / 2, 0, ((viewportWidth / 2) * 1.5) - 50, (viewportHeight / 2) - 20, viewportWidth, (viewportHeight / 2) - 20]);
  layer.batchDraw();
};

layer.add(bluePoly, whitePoly, yellowLine);
stage.add(layer);

window.addEventListener('resize', () => {
  stage.width(window.innerWidth);
  stage.height(document.querySelector('.content-container').clientHeight / 2);

  const viewportWidth = stage.width();
  const viewportHeight = stage.height();

  bluePoly.points([0, 0, viewportWidth, 0, viewportWidth / 2, viewportHeight / 2, 0, viewportHeight / 2]);
  whitePoly.points([0, 0, viewportWidth / 2, 0, (viewportWidth / 2) * 1.5, viewportHeight / 2, 0, viewportHeight / 2]);
  yellowLine.points([0, 0, viewportWidth / 2, 0, ((viewportWidth / 2) * 1.5) - 50, (viewportHeight / 2) - 20, viewportWidth, (viewportHeight / 2) - 20]);

  redrawYellowLine();
  layer.batchDraw();
});

Solution

  • Relying on a timer to stop an animation will almost always make the actual stop unpredictable. Luckily Konva indeed offers an alternative: Tweens.

    The main benefits are:

    • You can set a specific duration for your animation
    • Define how the object should look at the end of the animation

    As your interested in animating the dashoffset, try the following lines right after adding yellowLine to the layer.

    let tween = new Konva.Tween({
        node: yellowLine,
        duration: 2,
        dashOffset: 0
    });
    tween.play();