Skip to main content

Text Animations Tutorial

Note: this feature is only available from Konva v10.0.0.

Konva provides powerful text animation capabilities through the charRenderFunc property. This function allows you to customize how each character is rendered, enabling character-by-character animations and effects.

var text = new Konva.Text({
x: 10,
y: 10,
text: 'AB',
fontSize: 20,
charRenderFunc: function ({ context, index }) {
if (index === 1) {
// shift only the second character
context.translate(0, 10);
}
},
});

The charRenderFunc receives a context object with the following parameters:

  • char - The actual character string being rendered
  • index - Zero-based index of the character in the entire text
  • x - X position where the character will be rendered
  • y - Y position where the character will be rendered
  • lineIndex - Zero-based index of the line containing this character
  • column - Zero-based column position within the current line
  • isLastInLine - Boolean indicating if this is the last character in its line
  • width - Width of the character
  • context - Canvas 2D rendering context for applying transformations, opacity, colors, etc.

This allows you to apply transformations, opacity changes, or other effects to individual characters based on their position and properties.

import Konva from 'konva';

const stage = new Konva.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight,
});

const layer = new Konva.Layer();
stage.add(layer);


// we will store the opacity of each character in an array

const charOpacities = [];

const textNode = new Konva.Text({
  x: window.innerWidth / 2 - 100,
  y: window.innerHeight / 2 - 20,
  text: 'ANIMATION',
  fontSize: 40,
  fontFamily: 'Arial',
  fill: '#333',
  charRenderFunc: function ({ context, index }) {
    context.globalAlpha = charOpacities[index];
  },
});
layer.add(textNode);

const anim = new Konva.Animation(function(frame) {
  const time = frame.time;
  const cycleDuration = 4000; // 4 seconds total cycle

  const fadeInDuration = 1500; // 1.5 seconds to fade in all

  const holdDuration = 1000; // 1 second hold

  const fadeOutDuration = 1500; // 1.5 seconds to fade out all

  
  const cycleTime = time % cycleDuration;
  
  for (let i = 0; i < textNode.text().length; i++) {
    const charDelay = i * 150; // 150ms delay between characters

    
    if (cycleTime < fadeInDuration) {
      // Fade in phase

      const charStartTime = charDelay;
      const charFadeTime = Math.max(0, cycleTime - charStartTime);
      charOpacities[i] = Math.min(1, charFadeTime / 300);
    } else if (cycleTime < fadeInDuration + holdDuration) {
      // Hold phase - all characters visible

      charOpacities[i] = 1;
    } else {
      // Fade out phase

      const fadeOutStart = fadeInDuration + holdDuration;
      const charFadeOutDelay = i * 150; // Same order as fade in

      const charFadeOutTime = Math.max(0, cycleTime - fadeOutStart - charFadeOutDelay);
      charOpacities[i] = Math.max(0, 1 - charFadeOutTime / 300);
    }
  }
}, layer);

anim.start();