واجهة برمجة تطبيقات requestAnimationFrame - الآن بدقة أقل من مللي ثانية

إلماري هيكينين

إذا كنت تستخدم تطبيق requestAnimationFrame، لا شك أنّك استمتعت بمزامنة الألوان مع معدّل تحديث الشاشة، ما يؤدي إلى إنشاء صور متحركة عالية الدقة قدر الإمكان. بالإضافة إلى ذلك، يمكنك توفير ضوضاء مروحة وحدة المعالجة المركزية (CPU) وطاقة البطارية عند التبديل إلى علامة تبويب أخرى.

ومع ذلك، قد يطرأ تغيير على جزء من واجهة برمجة التطبيقات. يتغير الطابع الزمني الذي تم إدخاله إلى دالة معاودة الاتصال من طابع زمني نموذجي يشبه Date.now() إلى قياس عالي الدقة للنقطة العائمة بالمللي ثانية منذ فتح الصفحة. إذا كنت تستخدم هذه القيمة، ستحتاج إلى تعديل رمزك، وذلك وفقًا للشرح التالي.

للتوضيح فقط، إليك ما أتحدث عنه:

// assuming requestAnimationFrame method has been normalized for all vendor prefixes..
requestAnimationFrame(function(timestamp){
    // the value of timestamp is changing
});

إذا كنت تستخدم السمة requestAnimFrame الشائعة الواردة هنا، يعني ذلك أنك لا تستخدم قيمة الطابع الزمني. لم يتبقَّ سوى 10 أفراد كحد أقصى. :)

أهمية

لماذا؟ يساعدك معدّل التردّد الصوتي في الحصول على 60 لقطة في الثانية المثالية، 60 لقطة في الثانية تعني 16.7 ملي ثانية لكل إطار. ولكن القياس باستخدام عدد صحيح بالملي ثانية يعني أن لدينا دقة تبلغ 1/16 لكل ما نريد مراقبته واستهدافه.

مقارنة الرسم البياني للقيمتين 16 ملّي ثانية مقابل 16 ملي ثانية لعدد صحيح صحيح

كما ترى أعلاه، يمثل الشريط الأزرق الحد الأقصى لمقدار الوقت الذي يجب عليك فيه القيام بكل عملك قبل رسم إطار جديد (60 إطارًا في الثانية). أنت على الأرجح تفعل أكثر من 16 شيئًا، ولكن مع عدد صحيح بالمللي ثانية، لا يمكنك سوى جدولة تلك الزيادات الكبيرة جدًا وقياسها. هذا ليس جيدًا بما فيه الكفاية.

يمكنك الاستعانة بالموقّت العالي الدقة لحلّ هذه المشكلة من خلال تقديم صورة أكثر دقّة:

Date.now()         //  1337376068250
performance.now()  //  20303.427000007

يتوفر المؤقت عالي الدقة حاليًا في Chrome باسم window.performance.webkitNow()، وهذه القيمة تساوي بشكل عام قيمة الوسيطة الجديدة التي تم تمريرها في استدعاء rAF. وبعد نقل المواصفات من جديد إلى المعايير القياسية، ستلغي الطريقة البادئة وتصبح متاحة من خلال performance.now().

ستلاحظ أيضًا القيمتين أعلاه هما العديد من الترتيبات ذات الأحجام المختلفة. تمثّل القيمة performance.now() قياسًا بالملي ثانية للنقطة العائمة منذ بدء تحميل تلك الصفحة تحديدًا (وتحديدًا performance.navigationStart).

قيد الاستخدام

تكمن المشكلة الرئيسية التي تتمثّل في الاقتصاص في مكتبات الصور المتحركة التي تستخدم نمط التصميم هذا:

function MyAnimation(duration) {
    this.startTime = Date.now();
    this.duration = duration;
    requestAnimFrame(this.tick.bind(this));
}
MyAnimation.prototype.tick = function(time) {
    var now = Date.now();
    if (time > now) {
        this.dispatchEvent("ended");
        return;
    }
    ...
    requestAnimFrame(this.tick.bind(this));
}

من السهل جدًا التعديل لإصلاح هذه المشكلة... يمكنك زيادة startTime وnow لاستخدام window.performance.now().

this.startTime = window.performance.now ?
                    (performance.now() + performance.timing.navigationStart) :
                    Date.now();

وهذا التنفيذ بسيط إلى حد ما، ولا يستخدم طريقة now() مسبَقة، كما يفترض إتاحة استخدام Date.now()، وهو غير متوفّر في الإصدار IE8.

اكتشاف الميزات

إذا كنت لا تستخدم النمط أعلاه وتريد فقط تحديد نوع قيمة معاودة الاتصال التي تحصل عليها، يمكنك استخدام هذا الأسلوب:

requestAnimationFrame(function(timestamp){

    if (timestamp < 1e12){
        // .. high resolution timer
    } else {
        // integer milliseconds since unix epoch
    }

    // ...

إنّ التحقّق من الرقم if (timestamp < 1e12) هو اختبار بطيء لمعرفة حجم الرقم الذي نتعامل معه. من الناحية الفنية، قد تكون هذه الطريقة موجبة خاطئة، ولكن فقط إذا كانت صفحة الويب مفتوحة لمدة 30 عامًا. ولكن لا يمكننا اختبار ما إذا كان يمثل رقم نقطة عائمة (بدلاً من أن يكون مطابقًا لعدد صحيح). اطلب عددًا كافيًا من الموقتات العالية الدقة وستحصل على قيم صحيحة في وقت ما.

نخطط لدفع هذا التغيير في الإصدار 21 من Chrome، لذلك إذا كنت تستفيد حاليًا من معلمة معاودة الاتصال هذه، فاحرص على تحديث الرمز!