Dalej bawimy się requestAnimationFrame, które musimy lepiej poznać. Kontynuacja lekcji poprzednich, do dzieła!

Ok, poprzednio zrobiliśmy sobie takie coś:

let header = document.querySelector("#header");

let headerRect = header.getBoundingClientRect();

let leftpos = headerRect.left;

function moveHeader(timestamp){

  leftpos += 5;
  
  if((leftpos + header.offsetWidth) > window.innerWidth){
    return;
  } else {
    header.style.left = leftpos + 'px';
    requestAnimationFrame(moveHeader);
  }

}

requestAnimationFrame(moveHeader);

Teraz chcemy, aby ta animacja chodziła od lewej do prawej i z powrotem. Będzie nam potrzebne handle do animacji oraz koniec z wychodzeniem returnem, bo wtedy nie da się wywołać następnej komendy (np. zacznij inną animację).

Ok, nowy JS:

let header = document.querySelector("#header");

let headerRect = header.getBoundingClientRect();

let leftpos = headerRect.left;

let aniReq;

function moveHeaderRight(timestamp){

  leftpos += 5;
  
  if((leftpos + header.offsetWidth) > window.innerWidth){
    cancelAnimationFrame(aniReq);
    aniReq = requestAnimationFrame(moveHeaderLeft);
  } else {
    header.style.left = leftpos + 'px';
    requestAnimationFrame(moveHeaderRight);
  }

}

aniReq = requestAnimationFrame(moveHeaderRight);

To zadziała, musimy tylko napisać drugą animację:

function moveHeaderLeft(timestamp){

    leftpos -= 5;
    
    if((leftpos-5) <= 0){
      cancelAnimationFrame(aniReq);
      aniReq = requestAnimationFrame(moveHeaderRight);
    } else {
      header.style.left = leftpos + 'px';
      requestAnimationFrame(moveHeaderLeft);
    }
  
  }

Ale fajnie, mamy jedną zmienną (aniReq) do której pakujemy handle różnych animacji. I zawsze możemy każdą usunąć.

Ok, a do czego nam ten timestamp? Cóż, nie do tego, co by się wydawało, czyli tworzenie interwałów, choć możemy tak się pobawić:

let header = document.querySelector("#header");

// let headerRect = header.getBoundingClientRect();

// let leftpos = headerRect.left;

let initialFs = 16;

let aniReq;
let start = null;
function biggerFontsize(timestamp){
    
    if (start === null) {
        start = timestamp;
      }
      const elapsed = timestamp - start;

      if(elapsed >= 3000){
        start = null;
        console.log("I do action every 3 seconds...")
      }
      requestAnimationFrame(biggerFontsize);

}

Tam na dole wykomentowałem inne animacje. No niby mamy interwał, ale w zasadzie to jest odpalane co klatkę, nie korzysta z optymalizacji przez pętlę zdarzeń i setInterval, tylko co klatkę leci. Czyli słaby interes.

I albo nie damy requestAnimationFrame i się nam nie odpali, albo damy i odpali się co klatkę. Zresztą sprawdźmy:

function biggerFontsize(timestamp){
    
    if (start === null) {
        start = timestamp;
      }
      const elapsed = timestamp - start;

      if(elapsed >= 3000){
        start = null;
        console.log("I do action every 3 seconds...")
        requestAnimationFrame(biggerFontsize);
      }

}

aniReq = requestAnimationFrame(biggerFontsize);

No i zepsuliśmy „interwał”. Sprawdza, czy upłynęło 3 sekundy, nie upłynęło, nie odpala następnej klatki. Gdyby upłynęło, to by odpalił i taki „interwał” mamy w przykładzie powyżej, który wykonuje callback co klatkę.

To do czego nam ten elapsed time może się przydać?

Cóż, czasem możemy chcieć go do czegoś.

function biggerFontsize(timestamp){
    
    if (start === null) 
        start = timestamp;
      
    const elapsed = timestamp - start;
    const elapsedSeconds = Math.floor(elapsed/1000);
    const newFS = 16 + elapsedSeconds;

    header.style.fontSize = `${newFS}px`;

    requestAnimationFrame(biggerFontsize);

}

aniReq = requestAnimationFrame(biggerFontsize);

Tu ilość sekund, która upłynęła jest wartością wielkości czcionki. Głupi przykład, podam inny:

let header = document.querySelector("#header");

// let headerRect = header.getBoundingClientRect();

// let leftpos = headerRect.left;

let initialCol = `rgb(0,0,0)`;
header.style.color = initialCol;

let aniReq;
let start = null;
function changeColor(timestamp){
    
    if (start === null) 
        start = timestamp;
      
    let elapsed = timestamp - start;
    let elapsedDecimals = Math.floor(elapsed/20);

    if(elapsedDecimals > 255){
        return cancelAnimationFrame(reqAni);
    }

    header.style.color = `rgb(${elapsedDecimals},${elapsedDecimals},${elapsedDecimals})`;
   
    requestAnimationFrame(changeColor);

}

aniReq = requestAnimationFrame(changeColor);

Możemy dowolnie się tym bawić i dostosować czas. I ten czas, który upłynął jest argumentem do czegoś. Nie wygląda to zbyt efektownie, bo tutaj trzeba poćwiczyć, użyć to z hue-rotate albo czymś takim.

Można świetnie wyglądające rzeczy tym tworzyć. I jest to jedyny logiczny powód, dla którego timestamp jest przyjmowany jako callback do tej funkcji, inne nie mają sensu.