الوظيفة المصغّرة للصور المتحركة في Houdini's

تحسين الصور المتحركة في تطبيق الويب

النص المختصر (TL;DR): تتيح لك Animation Worklet كتابة صور متحركة ضرورية يتم تشغيلها. بالعدد الأصلي للقطات في الثانية في الجهاز للحصول على تجربة سلسة خالية من التشويه جعل الرسوم المتحركة أكثر مرونة ضد مشكلة السلسلة الرئيسية وقابلة للربط التمرير بدلاً من الوقت. Animation Worklet موجود في Chrome Canary (خلف "ميزات تجريبية للنظام الأساسي للويب" ) ونخطط لإصدار تجريبي المصدر من Chrome 71. يمكنك البدء في استخدامه إلى تحسين تدريجي اليوم.

هل Animation API أخرى؟

في الواقع لا، إنها امتداد لما لدينا بالفعل، ولأسباب وجيهة! هيا نبدأ من البداية. إذا أردت إضافة تأثير متحرك إلى أي عنصر DOM على الويب أمامك اليوم خياران: عمليات النقل إلى CSS في الانتقالات البسيطة من A إلى B، والرسوم المتحركة في CSS الرسوم المتحركة المستندة إلى الوقت والتي قد تكون دورية أكثر تعقيدًا وWeb Animations API (WAAPI) للصور المتحركة المعقدة تقريبًا. تبدو مصفوفة دعم WAAPI قاتمة للغاية، إنها في طريقها لأعلى. حتى ذلك الحين، هناك polyfill.

وما تشترك فيه كل هذه الطرق هو أنها عديمة الحالة مدفوعًا بالوقت. إلا أن بعض التأثيرات التي يحاول المطورون استخدامها ليست وقائم على الوقت أو عديم الحالة. على سبيل المثال، يرفرف المنظر السيئ السمعة، كما الذي يتضمنه الاسم، المستند إلى التمرير. تواجه اليوم صعوبة مدهشة في استخدام شريط تمرير ثابت للمنظر على الويب.

وماذا عن انعدام الحالة؟ فكر في شريط عناوين Chrome في Android، مثال. في حال التمرير للأسفل، يتم الانتقال خارج إطار العرض. لكن وثانيًا، تقوم بالتمرير لأعلى، سيعود، حتى لو كنت في منتصف الطريق أسفل تلك الصفحة. لا تعتمد الرسوم المتحركة على موضع التمرير فقط، ولكن أيضًا على اتجاه التمرير السابق. وهو يذكر.

هناك مشكلة أخرى وهي تصميم أشرطة التمرير. تشتهر بأنها غير نمطية - أو في أقل نمطًا بما فيه الكفاية. ماذا لو أردتُ أن يكون قط الناي شريط التمرير؟ مهما كان الأسلوب الذي تختاره، فإن إنشاء شريط تمرير مخصص ليس أو أداءً سهلاً.

المغزى هو أن كل هذه الأشياء مربكة ويصعب المستحيل التنفيذ بكفاءة. ويعتمد معظمها على الأحداث و/أو requestAnimationFrame، والذي قد يبقيك بمعدّل 60 لقطة في الثانية، حتى عندما تكون شاشتك قادر على التشغيل بمعدل 90 لقطة في الثانية أو 120 لقطة في الثانية أو أعلى واستخدام جزء من ميزانية إطار السلسلة الرئيسية الثمينة.

تعمل Animation Worklet على توسيع إمكانات مكدس الصور المتحركة على الويب لإنشاء هذا النوع من التأثيرات أسهل. قبل أن نتعمق في، دعونا نتأكد من أننا على اطلاع دائم بأساسيات الرسوم المتحركة.

لمحة تمهيدية حول الرسوم المتحركة والمخططات الزمنية

يستفيد كل من WAAPI وAnimation Worklet من الجداول الزمنية بشكل مكثف للسماح لك تنسيق الرسوم المتحركة والتأثيرات بالطريقة التي تريدها. هذا القسم عبارة عن تنشيط سريع للذاكرة أو مقدمة عن الجداول الزمنية وكيفية عملها مع الرسوم المتحركة.

يحتوي كل مستند على document.timeline. ويبدأ الرقم على 0 عندما يكون المستند تم إنشاؤه ويحسب وحدات المللي ثانية منذ بدء وجود المستند. كل تعمل الرسوم المتحركة للمستند بالنسبة إلى هذا الجدول الزمني.

لجعل الموضوع أكثر واقعية، دعونا نلقي نظرة على مقتطف WAAPI هذا.

const animation = new Animation(
  new KeyframeEffect(
    document.querySelector('#a'),
    [
      {
        transform: 'translateX(0)',
      },
      {
        transform: 'translateX(500px)',
      },
      {
        transform: 'translateY(500px)',
      },
    ],
    {
      delay: 3000,
      duration: 2000,
      iterations: 3,
    }
  ),
  document.timeline
);

animation.play();

عند استدعاء الدالة animation.play()، تستخدم الصور المتحركة currentTime للمخطط الزمني كوقت البدء. تكون مدة التأخير في الرسوم المتحركة 3000 ملي ثانية، مما يعني أن ستبدأ الصورة المتحركة (أو تصبح "نشطة") عندما يصل المخطط الزمني إلى "وقت البدء"

  • 3000. After that time, the animation engine will animate the given element from the first keyframe (translateX(0)), through all intermediate keyframes (translateX(500px)) all the way to the last keyframe (translateY(500px)) in exactly 2000ms, as prescribed by theالمدةoptions. Since we have a duration of 2000ms, we will reach the middle keyframe when the timeline'sالوقت الحاليisوقت البدء + 3000 + 1000and the last keyframe atوقت البدء + 3000 + 2000`. النقطة هي أن يتحكم المخطط الزمني في مكان وجودنا في الرسوم المتحركة!

بمجرد وصول الرسم المتحرك إلى الإطار الرئيسي الأخير، سينتقل مرة أخرى إلى الإطار الرئيسي الأول الإطار الرئيسي وبدء التكرار التالي للرسوم المتحركة. تكرر هذه العملية إجمالي 3 مرات منذ إعداد iterations: 3. إذا أردنا أن تظهر الرسوم المتحركة فقد نكتب iterations: Number.POSITIVE_INFINITY. إليك نتيجة الرمز البرمجي أعلاه.

فواجهة برمجة التطبيقات WAAPI قوية جدًا، وهناك العديد من الميزات الأخرى في واجهة برمجة التطبيقات هذه، مثل التخفيف، وبدء الإزاحة، وتقديرات الإطارات الرئيسية، وسلوك التعبئة الذي قد يضيع نطاق هذه المقالة للمزيد من المعلومات، ننصحك بقراءة هذه المقالة عن الصور المتحركة في CSS على CSS Tricks.

كتابة عمل رسوم متحركة

الآن بعد أن أصبح لدينا مفهوم الجداول الزمنية، يمكننا البدء في إلقاء نظرة على Animation Worklet وكيف يسمح لك العبث بالجداول الزمنية! الحركة لا تعتمد Worklet API فقط على WAAPI، ولكنها، في مفهوم الويب القابل للتوسع، معيار أساسي منخفض المستوى توضح كيفية عمل WAAPI. من حيث بناء الجملة، هما متشابهان بشكل لا يصدق:

Animation Worklet WAAPI
new WorkletAnimation(
  'passthrough',
  new KeyframeEffect(
    document.querySelector('#a'),
    [
      {
        transform: 'translateX(0)'
      },
      {
        transform: 'translateX(500px)'
      }
    ],
    {
      duration: 2000,
      iterations: Number.POSITIVE_INFINITY
    }
  ),
  document.timeline
).play();
      
        new Animation(

        new KeyframeEffect(
        document.querySelector('#a'),
        [
        {
        transform: 'translateX(0)'
        },
        {
        transform: 'translateX(500px)'
        }
        ],
        {
        duration: 2000,
        iterations: Number.POSITIVE_INFINITY
        }
        ),
        document.timeline
        ).play();
        

يكمن الاختلاف في المَعلمة الأولى، وهي اسم worklet. هو الذي يقود هذه الرسوم المتحركة.

رصد الميزات

Chrome هو المتصفح الأول الذي يوفر هذه الميزة، لذا يجب التأكد من لا يتوقع الرمز وجود AnimationWorklet فقط. لذا، قبل تحميل ملف يجب أن نكتشف ما إذا كان متصفح المستخدم يدعم AnimationWorklet عن طريق إجراء عملية تحقّق بسيطة:

if ('animationWorklet' in CSS) {
  // AnimationWorklet is supported!
}

جارٍ تحميل الوظيفة المصغّرة

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

نحتاج إلى التأكد من تحميل ملف صغير باسم "passthrough"، قبل الإعلان عن الرسوم المتحركة:

// index.html
await CSS.animationWorklet.addModule('passthrough-aw.js');
// ... WorkletAnimation initialization from above ...

// passthrough-aw.js
registerAnimator(
  'passthrough',
  class {
    animate(currentTime, effect) {
      effect.localTime = currentTime;
    }
  }
);

ما الذي يحدث هنا؟ نحن نسجِّل فصلاً كمتخصص في الصور المتحركة باستخدام استدعاء AnimationWorklet: registerAnimator()، مع تسميته "مرور" وهذا الاسم هو نفسه الذي استخدمناه في الدالة الإنشائية WorkletAnimation() أعلاه. بمجرد اكتمال التسجيل، فإن الوعد الذي تم إرجاعه في موعد أقصاه addModule() سيحل يمكننا البدء في إنشاء رسوم متحركة باستخدام هذا الوظيفة المصغّرة.

سيتم استدعاء طريقة animate() للمثيل في كل إطار يريد المتصفّح العرض، مع تمرير currentTime من المخطط الزمني للرسم المتحرك بالإضافة إلى التأثير الذي تتم معالجته حاليًا. لدينا واحد فقط التأثير، فإن KeyframeEffect ونستخدم currentTime لضبط تأثير localTime، ومن هنا أطلق على أداة الصور المتحركة هذه اسم "مرور". باستخدام هذا الرمز لـ العمل الصغير، يتصرف WAAPI وAnimationWorklet أعلاه بالضبط نفس الشيء، كما ترى في العرض التوضيحي.

الوقت

المعلمة currentTime لطريقة animate() هي currentTime مررنا إلى الدالة الإنشائية WorkletAnimation(). في الفترة السابقة سبيل المثال، لقد مررنا للتو بهذا الوقت إلى التأثير. ولكن نظرًا لأن هذا رمز JavaScript، ويمكننا تشويه الوقت 💫

function remap(minIn, maxIn, minOut, maxOut, v) {
  return ((v - minIn) / (maxIn - minIn)) * (maxOut - minOut) + minOut;
}
registerAnimator(
  'sin',
  class {
    animate(currentTime, effect) {
      effect.localTime = remap(
        -1,
        1,
        0,
        2000,
        Math.sin((currentTime * 2 * Math.PI) / 2000)
      );
    }
  }
);

سنستخدِم Math.sin() من currentTime، وسنعيد تخصيص هذه القيمة إلى. النطاق [0; 2000]، وهو النطاق الزمني الذي تم تحديد التأثير من أجله. الْآنْ تبدو الصورة المتحركة مختلفة تمامًا، بدون الحاجة إلى غيرت الإطارات الرئيسية أو خيارات الرسوم المتحركة. يمكن أن يكون التعليمة البرمجية معقدة بشكل عشوائي، وتسمح لك بتحديد التأثيرات التي وبأي ترتيب وإلى أي مدى.

خيارات أكثر من الخيارات

وقد ترغب في إعادة استخدام ملف عمل وتغيير أرقامه. لهذا السبب تسمح لك الدالة الإنشائية WorkletAnimation بتمرير كائن خيارات إلى الوظيفة المصغّرة:

registerAnimator(
  'factor',
  class {
    constructor(options = {}) {
      this.factor = options.factor || 1;
    }
    animate(currentTime, effect) {
      effect.localTime = currentTime * this.factor;
    }
  }
);

new WorkletAnimation(
  'factor',
  new KeyframeEffect(
    document.querySelector('#b'),
    [
      /* ... same keyframes as before ... */
    ],
    {
      duration: 2000,
      iterations: Number.POSITIVE_INFINITY,
    }
  ),
  document.timeline,
  {factor: 0.5}
).play();

في هذا المثال، يتم تشغيل كلتا المتحركتين بنفس الرمز، ولكن بخيارات مختلفة.

أعطِ الولاية المحلية!

كما أوضحتُ من قبل، فإن إحدى المشكلات الرئيسية التي تهدف الأداة المتحركة إلى حلها هي والرسوم المتحركة الخاصة بالحالة. يُسمح لصغيرات الصور المتحركة بالحفاظ على الحالة. ومع ذلك، الميزات الأساسية للوظائف الصغيرة هي أنه يمكن ترحيلها إلى منصة أو حتى إتلافها لتوفير الموارد، مما قد يؤدي أيضًا إلى تدميرها الولاية. لمنع فقدان الحالة، توفر الصورة المتحركة عنصر الجذب يسمى قبل تدمير عمل صغير يمكنك استخدامه لعرض حالة . سيتم تمرير هذا الكائن إلى الدالة الإنشائية عندما تكون الوظيفة المصغّرة تمت إعادة إنشاؤه. وعند الإنشاء الأوّلي، ستكون هذه المَعلمة undefined.

registerAnimator(
  'randomspin',
  class {
    constructor(options = {}, state = {}) {
      this.direction = state.direction || (Math.random() > 0.5 ? 1 : -1);
    }
    animate(currentTime, effect) {
      // Some math to make sure that `localTime` is always > 0.
      effect.localTime = 2000 + this.direction * (currentTime % 2000);
    }
    destroy() {
      return {
        direction: this.direction,
      };
    }
  }
);

في كل مرة تُحدّث فيها هذا العرض التوضيحي، تحصل على 50/50 الفرصة في أي اتجاه سيدور المربع. إذا كان المتصفح سيعطل الوظيفة الصغيرة ونقلها إلى مؤشر ترابط مختلف، فسيكون هناك استدعاء واحد (Math.random()) عند الإنشاء، ما قد يؤدي إلى تغيير مفاجئ في اتجاهك. للتأكد من عدم حدوث ذلك، نقوم بإعادة الرسوم المتحركة يتم اختيار الاتجاه عشوائيًا باعتباره state واستخدامه في الدالة الإنشائية، إذا توفّر.

الاطّلاع على سلسلة أحداث الزمان والمكان: ScrollTimeline

كما أوضح القسم السابق، تسمح لنا AnimationWorklet برمجيًا لتحديد كيفية تأثير تقدم الجدول الزمني على تأثيرات الرسوم المتحركة. ولكن حتى الآن، كان جدولنا الزمني دائمًا document.timeline، والذي الوقت وتتبعه.

يتيح لك "ScrollTimeline" إمكانات جديدة ويتيح لك إنشاء صور متحركة. من خلال التمرير بدلاً من الوقت. سنعيد استخدام "مرور" الوظيفة المصغّرة لهذا الغرض العرض التوضيحي:

new WorkletAnimation(
  'passthrough',
  new KeyframeEffect(
    document.querySelector('#a'),
    [
      {
        transform: 'translateX(0)',
      },
      {
        transform: 'translateX(500px)',
      },
    ],
    {
      duration: 2000,
      fill: 'both',
    }
  ),
  new ScrollTimeline({
    scrollSource: document.querySelector('main'),
    orientation: 'vertical', // "horizontal" or "vertical".
    timeRange: 2000,
  })
).play();

بدلاً من تجاوز document.timeline، ننشئ ScrollTimeline جديدًا. ربما تكون قد خمنت ذلك، ScrollTimeline لا يستخدم الوقت ولكن موضع تمرير scrollSource لضبط currentTime في الوظيفة المصغّرة يشير الانتقال إلى الأعلى (أو إلى اليسار) إلى currentTime = 0، بينما يتم تمريره وصولاً إلى الجزء السفلي (أو لليمين) يؤدي إلى تعيين currentTime على timeRange إذا قمت بالتمرير في المربع في العرض التوضيحي، يمكنك للتحكم في موضع المربع الأحمر.

إذا أنشأت ScrollTimeline باستخدام عنصر لا يمكن التنقّل في الصفحة إليه، currentTime للمخطط الزمني ستكون NaN. لذلك خاصة مع التصميم سريع الاستجابة في عليك دائمًا الاستعداد لاستخدام NaN باعتباره currentTime. غالبًا ما معقول من الإعداد الافتراضي على القيمة 0.

يُعد ربط الرسوم المتحركة بموضع التمرير شيئًا مطلوبًا منذ فترة طويلة، ولكنّه لم يتمّ تحقيق هذا المستوى من الدقة (بصرف النظر عن أسلوب صعب باستخدام CSS3D). تتيح Animation Worklet تغيير هذه التأثيرات تنفيذها بطريقة مباشرة مع تحقيق أداء عالٍ. مثل: تأثير تمرير اختلاف المنظر مثل هذا للعرض التوضيحي أنه يستغرق الآن سطرين فقط لتحديد الرسوم المتحركة القائمة على التمرير.

الخيارات المتقدمة

العملات الصغيرة

الأدوات العمليّة هي سياقات JavaScript ذات نطاق معزول وواجهة برمجة تطبيقات صغيرة جدًا. السطح. يتيح السطح الصغير لواجهة برمجة التطبيقات إجراء تحسين أكثر فعالية من المتصفح، وخاصةً على الأجهزة ذات المواصفات المنخفضة. بالإضافة إلى ذلك، لا يتم ربط الوظائف الصغيرة تكرارًا لحدث معين، ولكن يمكن نقلها بين سلاسل المحادثات حسب الضرورة. هذا هو مهم بشكل خاص لـ AnimationWorklet.

أداة NSync

قد تعرف أن بعض خصائص CSS يمكن تحريكها بسرعة، في حين أن البعض الآخر لا. تحتاج بعض الخصائص فقط إلى بعض العمل على وحدة معالجة الرسومات لجعلها متحركة، بينما تحتاج خصائص أخرى فرض إعادة تخطيط المستند بأكمله على المتصفح

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

ضربة على المعصم

عادة ما تكون هناك عملية مكون واحدة فقط يمكن مشاركتها عبر علامات تبويب متعددة، حيث إن وحدة معالجة الرسومات تعد موردًا عالي الجودة. إذا حصلت على بطريقة أو بأخرى، يتوقّف المتصفح بالكامل ويصبح غير مستجيب البيانات التي أدخلها المستخدم ويجب تجنب حدوث ذلك بأي ثمن. إذن ماذا يحدث إذا كان لا يمكن لـ Worklet تقديم البيانات التي يحتاجها المُنشئ في الوقت المناسب ليكون الإطار تم عرضه؟

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

الخاتمة

هناك العديد من الواجهات في AnimationWorklet والفوائد التي توفّرها على الويب. وتتمثل المزايا الواضحة في قدرة أكبر على التحكم في الرسوم المتحركة وطرق جديدة للقيادة والرسوم المتحركة لإضفاء مستوى جديد من الدقة المرئية على الويب. لكن واجهات برمجة التطبيقات التصميم أيضًا بجعل تطبيقك أكثر مرونة في مواجهة العطل مع الوصول إلى كل الأشياء الجديدة في نفس الوقت.

تتوفر Animation Worklet في إصدار Canary ونسعى إلى مرحلة التجربة والتقييم الإصدار 71 من Chrome نتطلّع بفارغ الصبر إلى تلقّي تجارب جديدة رائعة على الويب حول ما يمكننا تحسينه. تتوفر أيضًا polyfill توفّر لك واجهة برمجة التطبيقات نفسها، ولكنها لا توفر ميزة عزل الأداء.

يُرجى العلم أنّ عمليات النقل في CSS والصور المتحركة في CSS لا تزال صالحة. خيارات ويمكن أن يكون أبسط بكثير في الرسوم المتحركة الأساسية. ولكن إذا كنت بحاجة إلى الذهاب رائع، AnimationWorklet معك الدعم!