تعد الرسوم المتحركة السلسة والأداء ضرورية في تطبيقات الويب الحديثة. ومع ذلك، فإن إدارتها بشكل غير صحيح يمكن أن يؤدي إلى زيادة التحميل على سلسلة المحادثات الرئيسية للمتصفح، مما يتسبب في ضعف الأداء والرسوم المتحركة غير المرغوب فيها. requestAnimationFrame (rAF) عبارة عن واجهة برمجة تطبيقات للمتصفح مصممة لمزامنة الرسوم المتحركة مع معدل تحديث الشاشة، مما يضمن حركة أكثر سلاسة مقارنة بالبدائل مثل setTimeout. لكن استخدام rAF بكفاءة يتطلب تخطيطًا دقيقًا، خاصة عند التعامل مع العديد من الرسوم المتحركة.
في هذه المقالة، سوف نستكشف كيفية تحسين requestAnimationFrame من خلال إدارة الرسوم المتحركة بشكل مركزي، وإدخال التحكم في FPS، والحفاظ على استجابة الموضوع الرئيسي للمتصفح.
تعتبر الإطارات في الثانية (FPS) أمرًا بالغ الأهمية عند مناقشة أداء الرسوم المتحركة. يتم تحديث معظم الشاشات بمعدل 60 إطارًا في الثانية، مما يعني أنه يتم استدعاء requestAnimationFrame 60 مرة في الثانية. للحفاظ على الرسوم المتحركة السلسة، يجب على المتصفح إكمال عمله خلال حوالي 16.67 مللي ثانية لكل إطار.
إذا تم تشغيل عدد كبير جدًا من المهام خلال إطار واحد، فقد يفقد المتصفح وقت الإطار المستهدف، مما يتسبب في تقطع الإطارات أو سقوطها. يمكن أن يساعد خفض معدل الإطارات في الثانية لبعض الرسوم المتحركة في تقليل الحمل على الموضوع الرئيسي، مما يوفر توازنًا بين الأداء والسلاسة.
لإدارة الرسوم المتحركة بشكل أكثر كفاءة، يمكننا مركزية التعامل معها من خلال حلقة مشتركة بدلاً من وجود عدة استدعاءات requestAnimationFrame متناثرة في جميع أنحاء التعليمات البرمجية. يقلل النهج المركزي من المكالمات المتكررة ويسهل إضافة التحكم في FPS.
تسمح لنا فئة AnimationManager التالية بتسجيل وإلغاء تسجيل مهام الرسوم المتحركة أثناء التحكم في FPS المستهدف. افتراضيًا، نهدف إلى الوصول إلى 60 إطارًا في الثانية، ولكن يمكن تعديل ذلك وفقًا لاحتياجات الأداء.
class AnimationManager { private tasks: Set= 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();
في هذا الإعداد، نقوم بحساب deltaTime بين الإطارات لتحديد ما إذا كان الوقت الكافي قد مر للتحديث التالي استنادًا إلى FPS المستهدف. يتيح لنا ذلك تقليل وتيرة التحديثات لضمان عدم تحميل سلسلة المحادثات الرئيسية للمتصفح بشكل زائد.
لنقم بإنشاء مثال حيث نقوم بتحريك ثلاثة مربعات، كل منها برسوم متحركة مختلفة: واحد يتغير لونه، وآخر يتغير لونه، والثالث يدور.
إليك HTML:
إليك ملف CSS:
.animated-box { width: 100px; height: 100px; background-color: #3498db; transition: transform 0.1s ease; }
الآن، سنضيف JavaScript لتحريك كل مربع بخاصية مختلفة. سيتم تغيير حجم أحدهما، والآخر سيغير لونه، والثالث سوف يدور.
الخطوة 1: إضافة الاستيفاء الخطي (lerp)
يعد الاستيفاء الخطي (lerp) أسلوبًا شائعًا يستخدم في الرسوم المتحركة للانتقال بسلاسة بين قيمتين. فهو يساعد على إنشاء تقدم تدريجي وسلس، مما يجعله مثاليًا لتوسيع نطاق الخصائص أو نقلها أو تغييرها بمرور الوقت. تأخذ الدالة ثلاث معلمات: قيمة البداية، وقيمة النهاية، والوقت الطبيعي (t)، الذي يحدد مدى طول الفترة الانتقالية.
function lerp(start: number, end: number, t: number): number { return start (end - start) * t; }
الخطوة 2: تحجيم الرسوم المتحركة
نبدأ بإنشاء دالة لتحريك تحجيم المربع الأول:
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); }
الخطوة 3: الرسوم المتحركة الملونة
بعد ذلك، نقوم بتحريك تغيير لون المربع الثاني:
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); }
الخطوة 4: الرسوم المتحركة التناوبية
أخيرًا، قمنا بإنشاء دالة لتدوير المربع الثالث:
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); }
الخطوة 5: بدء الرسوم المتحركة
أخيرًا، يمكننا بدء الرسوم المتحركة للمربعات الثلاثة:
// 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
عند استخدام requestAnimationFrame، من الضروري أن تضع في اعتبارك أن الرسوم المتحركة تعمل على الموضوع الرئيسي للمتصفح. يمكن أن يؤدي التحميل الزائد على الموضوع الرئيسي بعدد كبير جدًا من المهام إلى فقدان المتصفح لإطارات الرسوم المتحركة، مما يؤدي إلى التأتأة. ولهذا السبب فإن تحسين الرسوم المتحركة الخاصة بك باستخدام أدوات مثل مدير الرسوم المتحركة المركزي والتحكم في FPS يمكن أن يساعد في الحفاظ على السلاسة، حتى مع الرسوم المتحركة المتعددة.
تتطلب إدارة الرسوم المتحركة بكفاءة في JavaScript أكثر من مجرد استخدام requestAnimationFrame. من خلال مركزية الرسوم المتحركة والتحكم في FPS، يمكنك ضمان رسوم متحركة أكثر سلاسة وأكثر أداء مع الحفاظ على استجابة الموضوع الرئيسي. في هذا المثال، أظهرنا كيفية التعامل مع الرسوم المتحركة المتعددة باستخدام AnimationManager واحد، مما يوضح كيفية تحسين الأداء وسهولة الاستخدام. بينما ركزنا على الحفاظ على FPS متسق من أجل البساطة، يمكن توسيع هذا الأسلوب للتعامل مع قيم FPS المختلفة لمختلف الرسوم المتحركة، على الرغم من أن ذلك كان خارج نطاق هذه المقالة.
جيثب ريبو: https://github.com/JBassx/rAF-optimization
StackBlitz: https://stackblitz.com/~/github.com/JBassx/rAF-optimization
لينكد إن: https://www.linkedin.com/in/josephciullo/
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3