Remember the sine scroller effect from 8-bit demos and intros in the 90's?
They were done in hundreds of lines of machine code, with hardware interrupts and pre-calculated sine tables. Here is an example in about 30 lines of pure JavaScript.
We need a bitmap image with a fixed-width font (also called mono-spaced or non-proportional). That means that each letter has the same width in pixels, which will greatly simplify our calculations. This paragraph uses a proportional font, so 'w' is wider than 'i'. The code example in the table uses a non-proportional font.
The font above was ripped from some intro for the legendary Commodore 64 (I found it in
the C-64 logo generator, but you can also just type in the alphabet in another font in an image editor and save it as a bitmap).
Now we're ready for the code:
<html> |
<body style='background-color:black'> |
<canvas id='myCanvas' width='600' height='800'></canvas> |
<script> |
const text = ' old school sine scroller another cool javascript tutorial'; |
const fontWidth = 54; |
const fontHeight = 71; |
const letters = 12; |
let x = []; |
let char = []; |
let wiggle = 0; |
let counter = 0; |
let position = letters; |
let bitmap = new Image(); |
bitmap.src = 'font.png'; |
let canvas = document.getElementById('myCanvas'); |
let context = canvas.getContext('2d'); |
for (let n = 0; n < letters; n++) { |
char[n] = text.charCodeAt(n) - 97; |
x[n] = n * fontWidth; |
} |
window.requestAnimationFrame(scroll); |
|
function scroll() { |
context.clearRect(0, 0, canvas.width, canvas.height); |
for (let n = 0; n < letters; n++) { |
let y = 100 + wiggle * Math.sin(n + counter / 6.28); |
context.drawImage(bitmap, char[n] * fontWidth, 0, fontWidth, fontHeight, x[n], y, fontWidth, fontHeight); |
x[n]--; |
if (x[n] < -fontWidth) { |
x[n] = (letters - 1) * fontWidth; |
char[n] = text.charCodeAt(position) - 97; |
position++; |
if (position > text.length) position = 0; |
} |
} |
if (counter > 200 && wiggle < 30) wiggle = wiggle + .1; |
counter++; |
window.requestAnimationFrame(scroll); |
} |
</script> |
</body></html> |
The main program just initializes the variables:
Lines 1-3: The HTML5 setup
5: The text of of our scroll. A couple of spaces in front so that it starts on the right hand side of the screen.
6-7: width and height of the font, in pixels
8: the number of letters to be scrolled
9: the array of the x coordinate of the letters
10: the character at a given position
11: the wiggle (amplitude) factor - we start with a flat line
12: time counter
13: the current position in the text
14-15: source image (not visible on the screen)
16-17: target canvas
18-21: set the first set of characters and their x coordinates
22: start the scroll animation
All the real action is in the scroll function:
25: clear the previous frame
26: for each letter:
27: calculate the y coordinate using the sine function
28: copy the character from the bitmap to the canvas.
This line is a little complicated, so let's look at the arguments in detail:
bitmap - source bitmap
char[n] * fontWidth, 0 - top-left corner coordinates of the source image of the n-th character
fontWidth, fontHeight - size of the source image (one character)
x[n], y - coordinates of the target location on the canvas
fontWidth, fontHeight - size of the copy (also one character)
29: move the letter to the left
30-33: if the letter moved off of the screen to the left, put it back on the right hand side and assign the next character from the scroll text
32: convert the JavaScript (ASCII) character to the 0-23 range: a is 0, b is 1 and so on.
34: restart if end of text reached
37: the wiggle factor determines the amplitude of the sine wave. We started with zero - flat line. When the counter reaches 200, we start increasing the amplitutde, until we reach the maximum value of 30.
38: increase the counter in each animation frame
39: request another frame
There is a lot of things you can add to this code:
- non-alpha character handling (numbers, comma, period etc.)
- control the speed of the scroll with the text (eg. speed up when the next letter is '@', slow down if it's '#')
- control the amplitude with the text
- calculate individual y coordinate for each vertical line of pixels (instead of each letter)
Enjoy!
Check out these programming tutorials:
JavaScript:
Optical illusion (18 lines)
Oldschool fire effect (20 lines)
Animated fractal (32 lines)
Physics engine for beginners
Starfield (21 lines)
Yin Yang with a twist (4 circles and 20 lines)
Interactive animated sprites
Your first program in JavaScript: you need 5 minutes and a notepad
Fractals in Excel
Python in Blender 3d:
Domino effect (10 lines)
Wrecking ball effect (14 lines)
3d fractal in Blender Python