Kaizokupuffball Kaizokupuffball - 1 month ago 9
Javascript Question

Canvas sinusoidal text scroller

I'm trying to create a sinusoidal text scrolling animation in HTML5 canvas, but I can't figure out how to animate each letter differently.

I know I can use

.split('')
to get an array that contains all the characters in the string. I tried using a
for
loop
for (var i = 0; i < chars.length; i++)
but that didn't do what I was expecting (all characters in the array were smooshed together). I was hoping somebody with the experience could help me out with the code and write comments in it, so that I can learn this.

What I already have is below. As you can see, it doesn't animate each letter. See this video for what I am trying to do.



// Canvas
var c = document.getElementById('c');
var ctx = c.getContext('2d');
var seconds = Date.now();
var offsetY = 220;
var offsetX = 490;
var chars = 'abc';
var amplitude = 50;
var textcolor ='#fff';
var backgroundcolor = '#000';

// Options
c.height = 500; // Canvas HEIGHT
c.width = 500; // Canvas WIDTH


function animate() {

var y = Math.floor((Date.now() - seconds) / 10) / 30;
var yPos = Math.sin((y)) * amplitude;

ctx.fillStyle = backgroundcolor;
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = textcolor;

ctx.fillText(chars, offsetX--, offsetY + yPos);

if (offsetX == 0) {
offsetX = 490;
}

// Loop it
requestAnimationFrame(animate);

}

// Start animation
requestAnimationFrame(animate);

<!doctype html>

<html>

<head>
<title>Sinus Scroller</title>
</head>

<body>

<canvas id="c">
</canvas>

</body>

</html>




Answer

It's desirable to warp the letters to the sine wave because the distance from one character to the next grows as the slope of the wave increases. If you avoid warping and simply implement the wave with constant speed in x and with y = sin(x) for each letter, you'll see inter-character gaps growing on the steep portions of the sine wave and shrinking near the optima.

At any rate, here is the simple implementation:

var text = 'Savor  the  delightful  flavor  of  Bubba-Cola',
    canvasWidth = 620,
    canvasHeight = 200,
    rightEdgeBuffer = 50;

WebFont.load({  // Web Font Loader: https://github.com/typekit/webfontloader
  google: {
    families: ['Source Sans Pro']
  },
  active: function () {  // Gets called when font loading is done.
    var canvas = document.getElementsByTagName('canvas')[0],
        context = canvas.getContext('2d'),
        yZero = canvasHeight / 2,      // Set axis position and amplitude
        amplitude = canvasHeight / 4,  // according to canvas dimensions.
        textColor ='#fff',
        backgroundColor = '#000';
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
    context.font = "32px 'Source Sans Pro', monospace";

    var pos = canvasWidth;  // Split the text into characters.
    var units = text.split('').map(function (char) {
      var width = context.measureText(char).width,
          unit = { char: char, width: width, pos: pos };
      pos += width;  // Calculate the pixel offset of each character.
      return unit;
    });

    var running = true,
        lapTime;  // Set this before the first animation call.

    function animate() {
      var currentTime = Date.now(),
          dp = (currentTime - lapTime) / 15;  // Displacement in pixels.
      lapTime = currentTime;
      context.fillStyle = backgroundColor;
      context.fillRect(0, 0, canvasWidth, canvasHeight);
      units.forEach(function (unit) {
        unit.pos -= dp;  // Update char position.
        if (unit.pos < -unit.width) {  // Wrap around from left to right.
          unit.pos += canvasWidth + rightEdgeBuffer;
        }
        var y = Math.sin(unit.pos / 45) * amplitude;
        context.fillStyle = textColor;
        context.fillText(unit.char, unit.pos, yZero + y);
      });
      if (running) {
        requestAnimationFrame(animate);
      }
    }

    document.getElementById('stopButton').onclick = function () {
      running = false;
    };

    lapTime = Date.now();
    requestAnimationFrame(animate);
  }
});
<script
 src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js"
></script>

<canvas></canvas>

<button id="stopButton"> stop </button>

Here is a more complete implementation with rectilinearly warped characters:

https://github.com/michaellaszlo/wavy-text

Comments