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.