Animasi yang lancar dan berprestasi adalah penting dalam aplikasi web moden. Walau bagaimanapun, mengurusnya secara tidak betul boleh membebankan utas utama penyemak imbas, menyebabkan prestasi lemah dan animasi janky. requestAnimationFrame (rAF) ialah API penyemak imbas yang direka untuk menyegerakkan animasi dengan kadar segar semula paparan, memastikan pergerakan yang lebih lancar berbanding alternatif seperti setTimeout. Tetapi menggunakan rAF dengan cekap memerlukan perancangan yang teliti, terutamanya apabila mengendalikan berbilang animasi.
Dalam artikel ini, kami akan meneroka cara mengoptimumkan requestAnimationFrame dengan memusatkan pengurusan animasi, memperkenalkan kawalan FPS dan memastikan urutan utama penyemak imbas responsif.
Bingkai sesaat (FPS) adalah penting apabila membincangkan prestasi animasi. Kebanyakan skrin dimuat semula pada 60 FPS, bermakna requestAnimationFrame dipanggil 60 kali sesaat. Untuk mengekalkan animasi yang lancar, penyemak imbas mesti menyelesaikan kerjanya dalam masa kira-kira 16.67 milisaat setiap bingkai.
Jika terlalu banyak tugas dijalankan dalam satu bingkai, penyemak imbas mungkin terlepas masa bingkai sasarannya, menyebabkan bingkai tergagap atau terjatuh. Menurunkan FPS untuk animasi tertentu boleh membantu mengurangkan beban pada utas utama, memberikan keseimbangan antara prestasi dan kelancaran.
Untuk mengurus animasi dengan lebih cekap, kami boleh memusatkan pengendaliannya dengan gelung kongsi dan bukannya mempunyai berbilang panggilan requestAnimationFrame bertaburan di seluruh kod. Pendekatan terpusat meminimumkan panggilan berlebihan dan menjadikannya lebih mudah untuk menambah kawalan FPS.
Kelas AnimationManager berikut membolehkan kami mendaftar dan menyahdaftar tugas animasi sambil mengawal FPS sasaran. Secara lalai, kami menyasarkan 60 FPS, tetapi ini boleh dilaraskan untuk keperluan prestasi.
class AnimationManager { private tasks: Set<FrameRequestCallback> = new Set(); private fps: number = 60; // Target FPS private lastFrameTime: number = performance.now(); private animationId: number | null = null; // Store the animation frame ID private run = (currentTime: number) => { const deltaTime = currentTime - this.lastFrameTime; // Ensure the tasks only run if enough time has passed to meet the target FPS if (deltaTime > 1000 / this.fps) { this.tasks.forEach((task) => task(currentTime)); this.lastFrameTime = currentTime; } this.animationId = requestAnimationFrame(this.run); }; public registerTask(task: FrameRequestCallback) { this.tasks.add(task); if (this.tasks.size === 1) { this.animationId = requestAnimationFrame(this.run); // Start the loop if this is the first task } } public unregisterTask(task: FrameRequestCallback) { this.tasks.delete(task); if (this.tasks.size === 0 && this.animationId !== null) { cancelAnimationFrame(this.animationId); // Stop the loop if no tasks remain this.animationId = null; // Reset the ID } } } export const animationManager = new AnimationManager();
Dalam persediaan ini, kami mengira deltaTime antara bingkai untuk menentukan sama ada masa yang mencukupi telah berlalu untuk kemas kini seterusnya berdasarkan FPS sasaran. Ini membolehkan kami mengecilkan kekerapan kemas kini untuk memastikan urutan utama penyemak imbas tidak terbeban.
Mari kita buat contoh di mana kita menganimasikan tiga kotak, setiap satu dengan animasi yang berbeza: satu skala, satu lagi bertukar warna dan yang ketiga berputar.
Ini HTML:
<div id="animate-box-1" class="animated-box"></div> <div id="animate-box-2" class="animated-box"></div> <div id="animate-box-3" class="animated-box"></div>
Ini CSS:
.animated-box { width: 100px; height: 100px; background-color: #3498db; transition: transform 0.1s ease; }
Sekarang, kami akan menambah JavaScript untuk menghidupkan setiap kotak dengan sifat yang berbeza. Satu akan berskala, satu lagi akan menukar warnanya dan yang ketiga akan berputar.
Langkah 1: Menambah Interpolasi Linear (lerp)
Interpolasi linear (lerp) ialah teknik biasa yang digunakan dalam animasi untuk peralihan antara dua nilai dengan lancar. Ia membantu mencipta perkembangan yang beransur-ansur dan lancar, menjadikannya sesuai untuk menskala, mengalihkan atau menukar sifat dari semasa ke semasa. Fungsi ini mengambil tiga parameter: nilai permulaan, nilai tamat dan masa ternormal (t), yang menentukan sejauh mana peralihan itu.
function lerp(start: number, end: number, t: number): number { return start + (end - start) * t; }
Langkah 2: Menskalakan Animasi
Kami mulakan dengan mencipta fungsi untuk menghidupkan penskalaan kotak pertama:
function animateScale( scaleBox: HTMLDivElement, startScale: number, endScale: number, speed: number ) { let scaleT = 0; function scale() { scaleT += speed; if (scaleT > 1) scaleT = 1; const currentScale = lerp(startScale, endScale, scaleT); scaleBox.style.transform = `scale(${currentScale})`; if (scaleT === 1) { animationManager.unregisterTask(scale); } } animationManager.registerTask(scale); }
Langkah 3: Animasi Warna
Seterusnya, kami menghidupkan perubahan warna kotak kedua:
function animateColor( colorBox: HTMLDivElement, startColor: number, endColor: number, speed: number ) { let colorT = 0; function color() { colorT += speed; if (colorT > 1) colorT = 1; const currentColor = Math.floor(lerp(startColor, endColor, colorT)); colorBox.style.backgroundColor = `rgb(${currentColor}, 100, 100)`; if (colorT === 1) { animationManager.unregisterTask(color); } } animationManager.registerTask(color); }
Langkah 4: Animasi Putaran
Akhir sekali, kami mencipta fungsi untuk memutar kotak ketiga:
function animateRotation( rotateBox: HTMLDivElement, startRotation: number, endRotation: number, speed: number ) { let rotationT = 0; function rotate() { rotationT += speed; // Increment progress if (rotationT > 1) rotationT = 1; const currentRotation = lerp(startRotation, endRotation, rotationT); rotateBox.style.transform = `rotate(${currentRotation}deg)`; // Unregister task once the animation completes if (rotationT === 1) { animationManager.unregisterTask(rotate); } } animationManager.registerTask(rotate); }
Langkah 5: Memulakan Animasi
Akhir sekali, kita boleh memulakan animasi untuk ketiga-tiga kotak:
// Selecting the elements const scaleBox = document.querySelector("#animate-box-1") as HTMLDivElement; const colorBox = document.querySelector("#animate-box-2") as HTMLDivElement; const rotateBox = document.querySelector("#animate-box-3") as HTMLDivElement; // Starting the animations animateScale(scaleBox, 1, 1.5, 0.02); // Scaling animation animateColor(colorBox, 0, 255, 0.01); // Color change animation animateRotation(rotateBox, 360, 1, 0.005); // Rotation animation
Apabila menggunakan requestAnimationFrame, adalah penting untuk diingat bahawa animasi berjalan pada urutan utama penyemak imbas. Melebihkan utas utama dengan terlalu banyak tugas boleh menyebabkan penyemak imbas terlepas bingkai animasi, mengakibatkan gagap. Inilah sebabnya mengapa mengoptimumkan animasi anda dengan alatan seperti pengurus animasi terpusat dan kawalan FPS boleh membantu mengekalkan kelancaran, walaupun dengan berbilang animasi.
Mengurus animasi dengan cekap dalam JavaScript memerlukan lebih daripada sekadar menggunakan requestAnimationFrame. Dengan memusatkan animasi dan mengawal FPS, anda boleh memastikan animasi yang lebih lancar dan berprestasi sambil memastikan urutan utama responsif. Dalam contoh ini, kami menunjukkan cara mengendalikan berbilang animasi dengan satu AnimationManager, menunjukkan cara mengoptimumkan prestasi dan kebolehgunaan. Walaupun kami menumpukan pada mengekalkan FPS yang konsisten untuk kesederhanaan, pendekatan ini boleh dikembangkan untuk mengendalikan nilai FPS yang berbeza untuk pelbagai animasi, walaupun itu di luar skop artikel ini.
Repo Github : https://github.com/JBassx/rAF-optimization
StackBlitz : https://stackblitz.com/~/github.com/JBassx/rAF-optimization
LinkedIn : https://www.linkedin.com/in/josephciullo/
Atas ialah kandungan terperinci Supercharge Animasi Web Anda: Optimumkan requestAnimationFrame Seperti Pro. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!