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

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

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

للتوضيح، إليك ما أقصده:

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

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

السبب

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

مقارنة بين الرسم البياني الذي يعرض 16 ملي ثانية ورسم بياني يعرض 16 ملي ثانية صحيحة

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

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

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

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

ستلاحظ أيضًا أنّ القيمتَين أعلاه تختلفان بعدّة orders of magnitude. 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، لذا إذا كنت تستفيد حاليًا من مَعلمة callback هذه، احرص على تعديل الرمز.