Sinus scroll w stylu 8-bitowym



Pamiętasz efekt sinus scroll z 8-bitowych demo i intro w latach 90-tych?

Były one tworzone w setkach linii assemblera, z przerwaniami sprzętowymi i obliczanymi z góry tablicami sinusa. A oto przykład w około 30 liniach czystego JavaScript.

Potrzebujemy obrazu bitmapowego z czcionką o stałej czerokości (inaczej mono-spaced lub nieproporcjonalnej). To oznacza, że każda litera ma tę samą szerokość w pikselach, co znacznie uprości nasze obliczenia. Ten paragraf używa fontu proporcjonalnego, więc 'w' jest szersze niż 'i'. Przykładowy kod w tabelce używa fontu nieproporcjonalnego.



Powyższa czcionka została zrippowana z jakiegoś intro na legendarny Commodore 64 (jest na stronie C-64 logo generator, ale możesz też po prostu wpisać alfabet w edytorze grafiki w innej czcionce i nagrać to jako bitmapę).

Teraz jesteśmy gotowi na kod:

<html>
<body style='background-color:black'>
<canvas id='myCanvaswidth='600height='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 = 0n < lettersn++) {
  char[n] = text.charCodeAt(n) - 97;
  x[n] = n * fontWidth;
  }
window.requestAnimationFrame(scroll);
 
function scroll() {
  context.clearRect(00canvas.widthcanvas.height);
  for (let n = 0n < lettersn++) {
    let y = 100 + wiggle * Math.sin(n + counter / 6.28);
    context.drawImage(bitmapchar[n] * fontWidth0fontWidthfontHeightx[n], yfontWidthfontHeight);
    x[n]--;
    if (x[n] < -fontWidth) {
      x[n] = (letters - 1) * fontWidth;
      char[n] = text.charCodeAt(position) - 97;
      position++;
      if (position > text.lengthposition = 0;
      }
    }
  if (counter > 200 && wiggle < 30wiggle = wiggle + .1;
  counter++;
  window.requestAnimationFrame(scroll);
  }
</script>
</body></html>


Główny program tylko inicjalizuje zmienne:

Linie 1-3: ustawienie HTML5
5: Tekst naszego scrolla. Kilka spacji na przodzie, by wychodził z prawej strony ekranu.
6-7: szerokość i wysykość fontu, w pikselach
8: liczba liter do scrollowania
9: tablica współrzędnych x liter
10: znak na danej pozycji
11: współczynnik amplitudy - zaczynamy od płaskiej linii
12: licznik czasu
13: bieżąca pozycja w tekście
14-15: obraz źródłowy (niewidoczny na ekranie)
16-17: docelowy canvas
18-21: ustawiamy pierwszy zestaw znaków i ich współrzędne x
22: rozpoczynamy animację scrolla

Cała prawdziwa akcja dzieje się w funkcji scroll:

25: wyczyść poprzednią klatkę
26: dla każdej litery:
27: oblicz współrzędną y używając funkcji sinus
28: skopiuj odpowiedni znak z bitmapy na canvas

Ta linia jest nieco skomplikowana, przyjrzymy się więc dokładnie argumentom:

bitmap - źródłowa bitmapa
char[n] * fontWidth, 0 - współrzędne lewego górnego rogu źródłowego obrazu n-tego znaku
fontWidth, fontHeight - rozmiar źródłowego obrazu (jeden znak)
x[n], y - współrzędne docelowe na canvas
fontWidth, fontHeight - rozmiar kopii (także jeden znak)

29: przesuń literę w lewo
30-33: jeżeli litera wyszła za ekran z lewej strony, ustaw ją z powrotem po prawej stronie i przypisz następny znak z tekstu
32: zamień znak JavaScript (ASCII) na zakres 0 do 23 : a to 0, b to 1 itd.
34: jeżeli dotarliśmy do końca tekstu, zaczynamy od nowa
37: ten współczynnik określa amplitudę sinusoidy. Zaczęliśmy od zera - płaskiej linii. Kiedy licznik osiąga 200, zacznamy zwiększać amplitudę, aż osiągniemy maksymalną wartość 30.
38: zwiększamy licznik w każdej klatce animacji
39: żądanie kolejnej klatki

Jest mnóstwo rzeczy, które możesz dodać do tego kodu:

- obsługa znaków nieliterowych (liczb, przecinka, kropki itd.)
- obsługa polskich liter
- kontrolowanie prędkości scrolla tekstem (np. przyspieszamy, gdy kolejny znak to '@', a zwalniamy, jeżeli to '#')
- kontrolowanie amplitudy tekstem
- obliczanie indywidualnej współrzędnej y dla każdej pionowej linii pikseli (zamiast dla każdej litery)
Powodzenia!


Inne samouczki:

Fraktale - 25 linii JavaScript


Złudzenie optyczne - 18 linii JavaScript


Rozbiajanie muru kulą - 14 linii kodu Python/Blender 3d


Efekt domino - 10 linii kodu Python/Blender 3d