اذهب إلى المحتوى

دليلك لتحريك الرسوم المتجهة SVG وفقًا لمواصفات SMIL


ولاء شبلاق

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

لمحة عامة

يمكن تحريك الرسوم المتجهة (SVG graphics) باستخدام عناصر التحريك المعرفة في مواصفات لغة التكامل المتزامن للوسائط المتعدّدة SMIL، تشمل تلك العناصر ما يلي:

  • <animate>: يسمح لك هذا العنصر بتحريك خاصيات HTML وخاصيات الكائنات على مدى فترة زمنية محددة.
  • <set>: وهو اختزال مناسب للتحريك، فهو يفيد في تعيين قيم التحريك لخاصيات HTML وخاصيات الكائنات مثل خاصية الظهور والرؤية.
  • <animateMotion>: يعمل على تحريك العنصر على امتداد مسار الحركة.
  • <animationColor>: تستطيع من خلال هذا العنصر تعديل القيم اللونية لخاصيات HTML وخاصيات الكائنات مع مرور الوقت. لاحظ إهمال العنصر <animateColor> لمجرد استخدام عنصر التحريك لاستهداف خصائص من الممكن أن تمتلك قيم لونية. بالرغم من ذلك لا يزال ذلك العنصر متاحًا في مواصفات SVG1.1، على العكس من SVG2 فهو مهمل ومحذوف بالكامل.

بالإضافة إلى عناصر التحريك المعرفة في مواصفات SMIL تحتوي الرسوم المتجهة SVG ملحقات متوافقة مع مواصفات SMIL والتي تحتوي على خاصيات توسع نطاق عمل العنصر <animateMotion> وعناصر إضافية أخرى. تشمل ملحقات الرسوم المتجهة SVG ما يلي:

  • <animateTransform>: يتيح لك تحريك إحدى خاصيات الرسوم المتجهة مع مرور الوقت، مثل خاصية transform.
  • path (خاصية HTML): تسمح بتحديد أي خاصية من صيغ بيانات مسار الرسوم المتجهة SVG في خاصية المسار لعنصر animateMotion، (التحريك اعتمادًا على SMIL فقط يسمح بتفرع صيغ بيانات الرسوم المتجهة خلال مسار). سنتطرق بالحديث أكثر عن animateMotion في الفقرات القادمة.
  • <mpath>: يستخدم بالاقتران مع عنصر animateMotion للإشارة إلى مسار حركة الذي سيستخدم كمسار لتحريك العناصر. مع العلم أن عنصر mpath مضمن داخل عنصر animationMotion قبل علامة الإغلاق.
  • <keypoints> (خاصية HTML): تستخدم كخاصية لـ animateMotion لتوفير تحكم دقيق في سرعة حركة العناصر في مساراتها.
  • <rotate> (خاصية HTML): تستخدم أيضًا كخاصية لـ animateMotion للتحكم في ما إذا كان الكائن يستجيب للدوران تلقائيًا بحيث يشير المحور السيني له في نفس الاتجاه (أو الاتجاه المعاكس) مثل متجه الظل الاتجاهي لمسار الحركة. هذه الخاصية هي مفتاح نجاح تحريك العناصر على امتداد مسار ما كما تتوقع للحركة أن تكون. مزيدًا من التوضيح في الجزئية الخاصة بشرح animationMotion.

حركة رسوم SVG من الممكن أن تكون مشابهة لرسوم CSS والانتقالات عبرها حسب طبيعتيهما. مفاتيح الحركة تُضبط وبالتالي تتحرك العناصر وتتغير الألوان …إلخ. مع ذلك بإمكان رسوم SVG القيام بأمور لا تستطيع رسوم CSS القيام بها وهذا ما سنتطرق له لاحقًا.

لماذا قد تستخدم الرسوم المُتجهة (SVG Animation)؟

من الممكن تصميم وتحريك الرسوم المتجهة (SVG) باستخدام CSS. بشكل أساسي التأثيرات الحركية التي تطبق على عناصر HTML من الممكن أيضًا تطبيقها على عناصر SVG. ولكن هناك بعض الخصائص التي يمكن تطبيقها على SVG ليست فعالة على CSS. مسار SVG على سبيل المثال يأتي مع مجموعة من البيانات (d=""‎ خاصية HTML) التي تعطي للمسار شكلًا. تلك البيانات يمكن تعريفها وتحريكها خلال SMIL ولكن الأمر لا ينطبق على CSS. ذلك لأن عناصر SVG تتصف بمجموعة من خاصيات HTML تُعرف بخاصيات للعرض. بعض تلك الخاصيات من الممكن معايرتها والتحكم فيها وتحريكها باستخدام CSS وهذا لا ينطبق على الخاصيات جميعها.

وهكذا ترى أن العديد من تأثيرات الحركة يمكن تطبيقها حاليًا باستخدام CSS. وأما الفجوات في استخدام SVG CSS من الممكن تجاوزها عند استخدام JavaScript أو SVG المستمدة من SMIL.

إذا كنت تفضل استخدام JavaScript فيستحسن أن تجرب snap.svg بواسطة Dmitry Baranovsky الذي يعد كمكتبة حافلة بالكثير من النصوص البرمجية الخاصة بـ SVG. ويمكنك الاطلاع على العديد من الأمثلة على ذلك. أما إذا كنت تفضل الخوض في مسار تعريفي أكثر بإمكانك استخدام عناصر التحريك التي سيتطرق لها هذا الدليل.

ميزة أخرى تميز SMIL عن JS هي أن رسوم JS لا تعمل عند تضمين رسوم SVG كصور img أو استعمالها كصورة خلفية (background-image) في CSS. رسوم SMIL تعمل في كلا الحالتين أو هكذا ينبغي. وهذه ميزة عظيمة. لربما ستجد نفسك قد اخترت SMIL عن غيرها من الخيارات بسبب تلك الميزة. ومن هنا فهذا الدليل سيؤدي دور المساعد لتتعلم كيف تستخدم SMIL بدءًا من اليوم.

دعم المتصفح والنسخ الاحتياطية

دعم المتصفح لرسوم SMIL لائق للغاية. حيث أنه يعمل في جميع المتصفحات باستثناء Internet Explorer و Opera Mini. للحصول على نظرة شاملة حول دعم المتصفح، يمكنك الرجوع إلى جدول التوافق.

إذا كنت بحاجة إلى توفير نسخة احتياطية لحركة الرسوم لـ SMIL، يمكنك اختبار دعم المتصفح أثناء التنقل باستخدام Modernizr. إذا لم يكن SMIL مدعومًا ، فيمكنك تقديم نوع من الاستعاضة (رسوم JavaScript متحركة، تجربة بديلة، …إلخ).

تحديد الهدف من الحركة باستخدام xlink: href

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

لتحديد الهدف يمكنك استخدام خاصية xlink: href. تعطي خاصية HTML هذه مرجع URI للعنصر المستهدف في التحريك والذي سيجري تعديله بمرور الوقت. يجب أن يكون العنصر المستهدف جزءًا من مستند SVG الحالي.

<rect id="cool_shape" ... />

<animate xlink:href="#cool_shape" ... />

إذا واجهت عناصر التحريك في SVG من قبل، فمن المحتمل أنك رأيتها متداخلة مع العنصر المفترض تحريكه. هذا ممكن تبعًا لمحددات:

  • إذا لم تتوفر خاصية xlink: hrefHTML، فسيكون العنصر المستهدف هو العنصر الأصل المباشر لعنصر الحركة الحالي.
<rect id="cool_shape" ... >

  <animate ... />

</rect>

لذلك إذا كنت تريد "تضمين" الرسم المتحرك في العنصر الذي ينطبق عليه، فبإمكانك القيام بذلك. وإذا كنت تريد أن تبقي الرسم المتحرك منفصل في مكان آخر في المستند، فيمكنك القيام بذلك أيضًا، وحدّد الهدف من كل حركة باستخدام xlink: href. كلا الطريقتين تعملان بشكل جيد.

تحديد الخاصية المستهدفة للحركة باستخدام attributeName وattributeType

تشترك جميع عناصر التحريك أيضًا في خاصية HTML أخرى وهي attributeName. تُستخدم attributeName لتحديد اسم خاصية HTML التي تقوم بتحريكها.

على سبيل المثال، إذا كنت ترغب في تحريك موضع مركز الدائرة <circle> على المحور السيني (x-axis)، حدد cx كقيمة لخاصية attributeName.

تأخذ attributeName قيمة واحدة فقط، ولا تقبل قيم متعددة، لذلك يمكنك تنشيط خاصية HTML واحدة فقط في كل مرة. إذا كنت ترغب في تحريك أكثر من خاصية HTML واحدة فأنت بحاجة إلى تعريف أكثر من تحريك واحد للعنصر. لربما لا توجد طريقة أخرى ولهذا لـ CSS أفضلية عن SMIL. ولكن مرة أخرى، نظرًا للقيم المحتملة لخاصيات الحركة الأخرى (والتي سنتطرق لها بعد ذلك)، فمن المنطقي تحديد اسم خاصية HTML واحد فقط في المرة الواحدة، وإلا فقد تصبح قيم الخاصيات الأخرى معقدة للغاية بحيث لا يمكن التعامل معها.

عند تحديد اسم خاصية HTML، يمكنك إضافة البادئة XMLNS (اختصار namespace XML) للإشارة إلى مجال اسم خاصية HTML. يمكن أيضًا تحديد مجال الاسم باستخدام الخاصية attributeType. على سبيل المثال، تعد بعض الخاصيات جزءًا من مساحة اسم CSS (مما يعني أنه يمكن العثور عليها كخاصية CSS أيضًا) والبعض الآخر XML فقط. يمكن العثور على جدول يوضح هذه الخاصيات هنا. خاصيات HTML في الجدول ليست كلها من خاصيات SVG إنها فقط تلك التي يمكن ضبطها باستخدام CSS. بعضها متاح بالفعل كخصائص CSS.

إذا لم تُعيّن قيمة attributeType بشكل صريح أو عُيّنت على "تلقائي"auto، يجب أن يبحث المتصفح أولاً من خلال قائمة خصائص CSS عن اسم خاصية مطابق، وإذا لم يُعثر على أي منهما سيبحث في مجال اسم XML الافتراضية للعنصر.

على سبيل المثال، يوضح المقتطف التالي التحكم في تحريك شفافية مستطيل SVG. نظرًا لأن خاصية HTML للشفافية opacity متوفرة أيضًا كخاصية CSS، تُعين attributeType على مجال اسم CSS:

<rect>
  <animate attributeType="CSS" attributeName="opacity" 
           from="1" to="0" dur="5s" repeatCount="indefinite" />
</rect>

سنتجاوز خاصيات الحركة الأخرى في الأمثلة التالية أدناه. جميع خاصيات التحريك متعارف عليها ما بين جميع عناصر التحريك ما لم يذكر خلاف ذلك.

تحريك خاصية HTML لعنصر من قيمة لأخرى على مدار فترة زمنية وتحديد الحالة النهائية: from وby وto وdur وfill

  • ابدأ بتحريك دائرة من موقع إلى آخر عن طريق تغيير قيمة الخاصية cx الخاصة بها (والتي تحدد قيمة x لمركزها).
  • عنصر <animate> سيتيح لك القيام بذلك حيث يستخدم لتحريك خاصية واحدة في كل مرة. عادةً ما تتحرك الخاصيات التي تأخذ قيم رقمية ولونية باستخدام هذا العنصر. تفقد الجدول للتعرف على الخاصيات التي يمكن تحريكها.
  • للتغيير من قيمة إلى أخرى خلال فترة زمنية يمكنك استخدام الخاصيات From وto وdur. ستحتاج أيضًأ إلى تحديد بداية الحركة عن طريق خاصية begin.
<circle id="my-circle" r="30" cx="50" cy="50" fill="orange" />

  <animate 
    xlink:href="#my-circle"
    attributeName="cx"
    from="50"
    to="450" 
    dur="1s"
    begin="click"
    fill="freeze" />

يوضح المثال السابق طريقة تعريف دائرة وتحريكها من مركزها من الموضع الأولي عند 50 وحدة ثم إلى الموضع الجديد 450 وحدة على امتداد محور السينات (x-axis).

تتحدد قيمة البداية begin بالنقر click أي أن الدائرة سوف تتحرك عند النقر عليها. ويمكنك أيضًا قيمة البداية كقيمة زمنية. على سبيل المثال، تعيين القيمة "begin="0s يعني أن حركة العنصر ستبدأ بمجرد تحميل الصفحة. بإمكانك تأخير حركة العنصر عند تحديد قيمة موجبة، مثلًا "begin="2s تعني أن حركة العنصر ستبدأ بعد ثانيتين من تحميل الصفحة.

الأكثر إثارةً هو أنه بإمكانك تعيين قيمة البداية begin مثل s1+click لتبدأ حركة العنصر خلال ثانية واحدة بعد النقر عليه! علاوةً على ذلك بإمكانك استخدام قيم أخرى تسمح لك بمزامنة العنصر بدون حساب المدة الزمنية وزمن التأخير للعناصر المتحركة الأخرى. ستعرف المزيد لاحقًا.

  • خاصية dur تكافئ animation-duration في CSS.
  • الخاصيتان from وto تشبهان الإطارين المفتاحيين (keyframes)‏ from وto في قاعدة @keyframe الخاصة بـ CSS:
@keyframes moveCircle {
  from { /* start value */ }
  to { /* end value */ }
}

أما خاصية HTML التعبئة fill (والتي تحمل نفس اسم خاصية HTML "التعبئة" التي تحدد لون العنصر لسوء الحظ) فهي تشبه خاصية وضع ملء الرسوم fill-mode property والتي تحدد ما إذا كان على العنصر أن يعود لحالته الأولية بعد انتهاء عملية التحريك. تتشابه قيم SVG مع تلك الموجودة في CSS، باستثناء اختلاف التسميات:

  • freeze: يُعني هذا التأثير بتجميد قيمة التأثير لآخر قيمة في الفترة النشطة. يبقى التأثير مجمدًا للفترة الزمنية المتبقية في المستند أو حتى إعادة تشغيل الحركة.
  • remove: يقوم هذا التأثير بإزالة تأثير الحركة (أي أنه لم يعُد مُطبقًا) عند انتهاء المدة النشطة للحركة. بعد انتهاء زمن الحركة لم يعد الهدف متأثرًا بالحركة ما لم يُعاد تشغيل الحركة من جديد.

حاول تغيير القيم في العرض الحي المباشر لترى كيف تتأثر الحركة:

تستخدم خاصية by لتعيين إزاحة نسبية للحركة. كما يوحي الاسم فبإمكانك استخدام الخاصية HTML لتحديد مقدار الإزاحة التي تريد للحركة أن تتقدم بها. يكون تأثير الخاصية by مرئيًا عند تقدمك خلال مدة الحركة في خطوات منفصلة، على غرار الطريقة المعمول بها في CSS عبر steps(). المكافئ لدالة steps()‎ في CSS هو "calcMode="discrete في SVG، وسنتطرق لخاصية calcMode لاحقًا في المقالة.

الحالة الأخرى عندما يكون تأثير by أكثر وضوحًا عند تحديد خاصية to. سيأتي مثال على ذلك لاحقًا. وأخيرًا وليس آخرًا، تأتي by بفائدة أخرى عند العمل مع الحركة المضافة والتراكمية.

إعادة تشغيل الحركة باستخدام restart

قد يكون من المفيد وقف إعادة تشغيل الحركة أثناء نشاطها. للقيام بذلك تمنح SVG خاصية restart المزودة بخيارات ثلاثة:

  • always: تمكنك من إعادة تشغيل الحركة في أي وقت حيث أنها القيمة الافتراضية.
  • whenNotActive: يمكن إعادة تشغيل الحركة فقط عندما لا تكون نشطة (أي بعد النهاية النشطة). أما محاولات إعادة تشغيل الحركة خلال المدة النشطة فيتم تجاهلها.
  • never: لا يمكن إعادة تشغيل العنصر لما تبقى من المدة البسيطة الحالية لحاوية الزمن. (في حالة SVG نظرًا لأن حاوية الوقت الأصلية هي جزء من مستند SVG، فلا يمكن إعادة تشغيل الحركة للفترة المتبقية من مدة المستند).

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

افترض أننا نريد تحريك موضع ولون دائرة، بحيث يتغير اللون في نهاية الحركة. بإمكانك القيام بذلك عن طريق تحديد قيمة begin لحركة تغيير اللون لتكون مساوية لمدة الحركة dur، هذه هي الطريقة التي نفعلها عادة في CSS.

SMIL مع ذلك، لديها ميزة لطيفة التعامل مع الأحداث. لقد ذكرنا قبل ذلك أن الخاصية begin تقبل قيمًا مثل click + 5s. تسمى هذه القيمة "قيمة الحدث (event value)، وتتكون في هذه الحالة من مرجع حدث متبوعًا بـ "قيمة الساعة". الجزء المثير للاهتمام هنا هو تسمية الجزء الثاني: "قيمة الساعة". لماذا ليست مجرد "قيمة زمنية"؟ حسنًا، الجواب هو أنه يمكنك استخدام قيمة الساعة حرفيًا مثل "10min" أو "01:33" أي ما يعادل "دقيقة و 33 ثانية"، أو حتى "02:30:03" (ساعتان، 30 دقيقة، و 3 ثوان). في وقت كتابة هذا التقرير، لم تُطبق قيم الساعة بالكامل في أي متصفح.

لذا، إذا أردنا العودة إلى العرض الحي السابق واستخدمت click + 01:30، إذا بدأ متصفح يدعمه، ستبدأ الحركة خلال دقيقة و 30 ثانية بعد النقر فوق الدائرة.

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

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

<circle id="orange-circle" r="30" cx="50" cy="50" fill="orange" />

<rect id="blue-rectangle" width="50" height="50" x="25" y="200" fill="#0099cc"></rect>

  <animate 
    xlink:href="#orange-circle"
    attributeName="cx"
    from="50"
    to="450" 
    dur="5s"
    begin="click"
    fill="freeze" 
    d="circ-anim" />

  <animate 
    xlink:href="#blue-rectangle"
    attributeName="x" 
    from="50"
    to="425" 
    dur="5s"
    begin="circ-anim.begin + 1s"
    fill="freeze" 
    id="rect-anim" />

البداية "begin="circ-anim.begin + 1s هي الجزء الذي يخبر المتصفح ببدء الحركة للمستطيل بعد ثانية واحدة من بداية الدائرة. تفقد العرض التوضيحي التالي:

يمكنك أيضًا بدء الحركة للمستطيل بعد انتهاء حركة الدائرة باستخدام حدث النهاية end بتتبع الشيفرة التالية:

<animate 
    xlink:href="#blue-rectangle"
    attributeName="x" 
    from="50"
    to="425" 
    dur="5s"
    begin="circ-anim.end"
    fill="freeze" 
    id="rect-anim"/>

يمكنك حتى بدء تشغيله قبل نهاية حركة الدائرة باستخدام الشيفرة التالية:

<animate 
    xlink:href="#blue-rectangle"
    attributeName="x" 
    from="50"
    to="425" 
    dur="5s"
    begin="circ-anim.end - 3s"
    fill="freeze" 
    id="rect-anim"/>

تكرار الحركة باستخدام repeatCount

إذا كنت ترغب في تشغيل الحركة أكثر من مرة، فيمكنك القيام بذلك باستخدام خاصية repeatCount. يمكنك تحديد عدد المرات التي ترغب في تكرارها، أو استخدام الكلمة المفتاحية الأساسية غير المحددة لجعلها تتكرر إلى ما لا نهاية infinite. لذلك، إذا أردنا تكرار الحركة للدائرة مرتين، ستكون الشيفرة كما يلي:

<animate 
    xlink:href="#orange-circle"
    attributeName="cx"
    from="50"
    to="450" 
    dur="5s"
    begin="click"
    repeatCount="2"
    fill="freeze" 
    id="circ-anim" />

يمكنك التحقق من العرض الحي هنا. في المثال الحي، عين عدد التكرار ليكون 2 على الدائرة، وبشكل غير نهائي في المربع infinite.

لاحظ طريقة إعادة تشغيل الحركة من القيمة الأولية from من القيمة بدلاً من القيمة التي وصلت إليها في نهاية الحركة. لسوء الحظ، لا تتضمن SMIL طريقة للرجوع إليها بين قيم البداية والنهاية مثل CSS التي تتيح لنا القيام بذلك. في CSS، تحدد خاصية اتجاه الحركة animation-direction ما إذا كانت الحركة يجب أن تنفذ بشكل معاكس على بعض أو كل الدورات أو التكرارات. Animation-direction: alternate: تعني القيمة البديلة أن عمليات تكرار دورة الحركة ذات القيم الفردية يتم تشغيلها في الاتجاه الطبيعي، وأما القيم الزوجية فتكون في اتجاه عكسي. هذا يعني أن الدورة الأولى ستكون من البداية إلى النهاية، ثم ستليها الدورة الثانية من النهاية إلى البداية، ثم الدورة الثالثة من البداية إلى النهاية، وهكذا.

في SMIL للقيام بذلك، يجب عليك استخدام JavaScript لتغيير قيم الخاصيات from وto بشكل صريح. كتب جون مكبارتلاند من Big Bite Creative منشورًا لفترة من الوقت يشرح فيه كيف فعل ذلك من أجل تحريك رمز القائمة الذي عمل عليه.

حل آخر هو تحديد القيمة النهائية كقيمة متوسطة ومن ثم تكون القيمة النهائية هي نفسها القيمة الأولية. على سبيل المثال، يمكنك تعيين الحركة لتبدأ من from قيمة وتنتهي بنفس القيمة مع to وكذلك باستثناء أنك تحدد ما كنت قد حددته ليكون قيمة نهائية كقيمة وسيطة بين from وto.

في CSS، سنفعل ذلك باستخدام شيء مثل هذا:

@keyframes example {
  from, to {
    left: 0;
  }

  50% {
    left: 300px;
  }
}

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

إليكم رسمًا جميلًا بسيطًا من قِبل Miles Elam يستخدم فيه بعض أوقات البدء المتأخرة:

تقييد وقت التكرار باستخدام repeatDur

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

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

على سبيل المثال، ستؤدي الشيفرة التالية إلى إيقاف تكرار الحركة لمدة دقيقة و 30 ثانية بعد بدء المستند:

<animate 
    xlink:href="#orange-circle"
    attributeName="cx"
    from="50"
    to="450" 
    dur="2s"
    begin="0s"
    repeatCount="indefinite"
    repeatDur="01:30" 
    fill="freeze" 
    id="circ-anim" />

وهنا تستطيع مشاهدة العرض الحي:

مزامنة الحركة على أساس عدد التكرارات

الآن لنرجع خطوة إلى المزامنة بين موضوعين في التحريك. في الواقع، في SMIL يمكنك مزامنة الحركة بحيث تبدأ حركة إحداهما بناء على عدد تكرار حركة الأخرى. على سبيل المثال، يمكنك بدء رسم متحرك بعد تكرار رقم آخر، بإضافة أو طرح مقدار الوقت الذي قد ترغب في إضافته.

المثال التالي يبدأ في تحريك للمستطيل في التكرار الثاني لحركة الدائرة:

<animate 
    xlink:href="#blue-rectangle"
    attributeName="x" 
    from="50"
    to="425" 
    dur="5s"
    begin="circ-anim.repeat(2)"
    fill="freeze" 
    id="rect-anim" />

يوضح العرض التالي بداية حركة المستطيل بعد ثانيتين من ثاني تكرار لحركة الدائرة:

يمكنك الاطلاع على نموذج لأساسيات SVG بإصدارها الثاني من إعداد David Eisenberg.

التحكم في قيم الإطارات المفتاحية للحركة: keyTimes وvalues

في CSS يمكننا تحديد القيم التي نريد أن تأخذها حركة الرسوم الخاصة بنا في إطار معين أثناء التحريك. على سبيل المثال، إذا كنت تحرك العنصر بإزاحة لليساربدلًا من تنشيطه من 0 إلى 300 مثلاً، يمكنك تحريكه بحيث يأخذ قيمًا معينة مثل هذا:

@keyframes example {
  0% {
    left: 0;
  }
  50% {
    left: 320px;
  }
  80% {
    left: 270px;
  }
  100% {
    left: 300px;
  }
}

الإطارات 0٪ و 20٪ و 80٪ و 100٪ هي إطارات الحركة، والقيم الموجودة في كل إطار تحدد قيمته. التأثير الموصوف أعلاه هو أحد العناصر التي ترتد عن الحائط، ثم تعود إلى الموضع النهائي.

في SMIL، يمكنك التحكم في القيم لكل إطار بطريقة مماثلة، لكن بناء الشيفرة مختلف تمامًا.

لتحديد إطارات مفتاحية، يمكنك استخدام خاصية keyTimes ثم لتحديد قيمة الخاصية المتحركة لكل إطار، يمكنك استخدام خاصية values. اصطلاحات التسمية في SMIL مريحة للغاية.

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

<animate 
    xlink:href="#orange-circle"
    attributeName="cx"
    from="50"
    to="450" 
    dur="2s"
    begin="click"
    values="50; 490; 350; 450"
    keyTimes="0; 0.5; 0.8; 1"
    fill="freeze" 
    id="circ-anim" />

إذن ماذا فعلنا هناك؟ أول ما يجب ملاحظته هنا هو تحديد أوقات الإطار الرئيسي والقيم الوسيطة كقوائم. خاصية keyTimes هي قائمة مفصولة بفواصل منقوطة لقيم الوقت المستخدمة للتحكم في سرعة الحركة. في كل مرة في القائمة يتوافق مع قيمة في قائمة خاصية values، ويحدد متى تُستخدم القيمة في وظيفة التحريك. تُحدد كل قيمة زمنية في قائمة keyTimes كقيمة نقطة عائمة بين 0 و 1 (ضمنًا)، مما يمثل إزاحة متناسبة في المدة البسيطة لعنصر الحركة. لذا تشبه أوقات المفاتيح تلك الموجودة في CSS، إلا أنه بدلًا من تحديدها كنسب مئوية، فإنك تحددها ككسر.

ما يلي هو العرض الحي للشيفرة أعلاه. انقر على الدائرة لبدء الحركة.

لاحظ أنه إذا استخدمت قائمةvalues، فإن الحركة ستطبق القيم بالترتيب على مدار فترة الحركة. إذا حددت قائمة values، فسيتم تجاهل أي من الخاصيات from وto وby. في هذه المرحلة، تجدر الإشارة أيضًا إلى أنه يمكنك استخدام خاصية values بدون خاصية keyTimes - تنتشر القيم تلقائيًا بالتساوي خلال الوقت لكل قيمة calcMode بخلاف paced (راجع القسم التالي).

التحكم في سرعة الحركة باستخدام التخفيف المخصص: calcMode و keySplines

سنتطرق إلى موازنة CSS-SMIL مرة أخرى لأن بناء SMIL ومفاهيمه سيكون أسهل بكثير لفهم ما إذا كنت ستعتاد بالفعل على التحريك بواسطة CSS.

في CSS، يمكنك اختيار تغيير سرعة الحركة الافتراضية الموحدة وتحديد وظيفة تخفيف مخصصة تتحكم في الحركة، باستخدام خاصية animation-timing-function. يمكن أن تكون وظيفة التوقيت واحدة من الكلمات المفتاحية المحددة مسبقًا، أو وظيفة مكعب cubic bezier. يمكن إنشاء ذلك باستخدام أداة مثل هذه المعدة بواسطة Lea Verou.

في SMIL، تُحدد سرعة الحركة باستخدام خاصية calcMode. سرعة الحركة الافتراضية هي linear لجميع عناصر الحركة باستثناء animateMotion (سنصل إليها لاحقًا في المقالة). بالإضافة إلى قيمة linear، يمكنك تعيين القيمة إلى: discrete أوpaced أوspline.

  • discrete: تحدد أن وظيفة الحركة ستقفز من قيمة إلى أخرى دون أي تقاطع. هذا مشابه للدالة steps()‎ في CSS.
  • paced: يشبه linear، إلا أنه سيتجاهل أي أوقات تقدم وسطية تحددها keyTimes. ويحسب المسافة بين القيم اللاحقة ويقسم الوقت وفقًا لذلك. إذا كانت قيمك كلها بترتيب خطي linear، فلن تلاحظ الفرق. ولكن إذا كانت ذهابًا وإيابًا، أو إذا كانت ألوانًا (والتي يعامل معها كقيم متجه ثلاثي الأبعاد)، فسترى بالتأكيد القيم الوسيطة. فيما يلي عرض حي من Amelia Bellamy-Royds، يُظهر الفرق بين قيم calcMode الثلاث المذكورة حتى الآن:

  • spline: وهي القيمة الرابعة المقبولة بواسطة calcMode. يتشابك من قيمة واحدة في قائمة values إلى القيمة التالية وفقًا لوظيفة الوقت المحددة بواسطة cubic Bezier spline. تعرف نقاط الشريحة في خاصية keyTimes، وتحددد نقاط التحكم لكل فاصل زمني في خاصية keySplines.

ربما لاحظت الخاصية HTML الجديدة في الجملة الأخيرة: خاصية keySplines. لذا، ماذا تفعل خاصية keySplines؟

مرة أخرى، إلى ما يعادلها في CSS.

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

@keyframes bounce {
    0% {
        top: 0;
        animation-timing-function: ease-in;
    }
    15% {
        top: 200px;
        animation-timing-function: ease-out;
    }
    30% {
        top: 70px;
        animation-timing-function: ease-in;
    }
    45% {
        top: 200px;
        animation-timing-function: ease-out;
    }
    60% {
        top: 120px;
        animation-timing-function: ease-in;
    }
    75% {
        top: 200px;
        animation-timing-function: ease-out;
    }
    90% {
        top: 170px;
        animation-timing-function: ease-in;
    }
    100% {
        top: 200px;
        animation-timing-function: ease-out;
    }
}

بدلاً من تسهيل الكلمات المفتاحية، كان بإمكاننا استخدام الدالة ()cubic-bezier ذات الصلة:

ease-in = cubic-bezier(0.47, 0, 0.745, 0.715)
ease-out = cubic-bezier(0.39, 0.575, 0.565, 1)

لنبدأ بتحديد الأوقات الرئيسية وقائمة القيم للدائرة البرتقالية لتخضع لنفس تأثير الارتداد:

<animate 
    xlink:href="#orange-circle"
    attributeName="cy"
    from="50"
    to="250" 
    dur="3s"
    begin="click"
    values="50; 250; 120;250; 170; 250; 210; 250"
    keyTimes="0; 0.15; 0.3; 0.45; 0.6; 0.75; 0.9; 1"
    fill="freeze" 
    id="circ-anim" />

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

تأخذ خاصية keySplines مجموعة من نقاط التحكم bezier المرتبطة بقائمة keyTimes، وتحديد وظيفة bezier المكعبة التي تتحكم في سرعة الفاصل الزمني. قيمة الخاصية HTML هي قائمة مفصولة بفواصل منقوطة لوصف نقطة التحكم. كل وصف لنقطة التحكم عبارة عن مجموعة من أربع قيم: y1 x1 x2 y2، تصف نقاط التحكم bezier لشريحة وقت واحدة. يجب أن تكون جميع القيم في النطاق من 0 إلى 1، وتستبعد الخاصية HTML ما لم تُعين calcMode على شكل spline.

بدلاً من أخذ دوال cubic-bezier كقيم، تأخذ keySplines إحداثيات نقطتي التحكم المستخدمة لرسم المنحنى. يمكن رؤية نقاط التحكم في لقطة الشاشة التالية المأخوذة من أداة Lea. تعرض لقطة الشاشة أيضًا إحداثيات كل نقطة، كل لون بنفس لون النقطة نفسها. بالنسبة لخاصية keySplines، هذه هي القيم التي سنستخدمها لتحديد سرعة الحركة الموجودة في keyframe.

يسمح SMIL بفصل هذه القيم إما بفواصل بمسافة بيضاء اختيارية أو بمسافة بيضاء فقط. قيم keyTimes التي تحدد الجزء المرتبط هي "نقاط الربط" bezier، وقيم keySplines هي نقاط التحكم. وبالتالي، يجب أن يكون هناك مجموعة واحدة أقل من نقاط التحكم من keyTimes.

01.png

إذا عدنا إلى مثال الكرة المرتدة، تُعرض إحداثيات نقطة التحكم للدالتين ease-in و ease-out كما هو موضح في الصور التالية:

02.png

03.png

لترجمة ذلك إلى عنصر متحرك في SVG، نحصل على الشيفرة التالي:

<animate 
    xlink:href="#orange-circle"
    attributeName="cy"
    from="50"
    to="250" 
    dur="3s"
    begin="click"
    values="50; 250; 120;250; 170; 250; 210; 250"
    keyTimes="0; 0.15; 0.3; 0.45; 0.6; 0.75; 0.9; 1"
    keySplines=".42 0 1 1;
                0 0 .59 1;
                .42 0 1 1;
                0 0 .59 1;
                .42 0 1 1;
                0 0 .59 1;
                .42 0 1 1;
                0 0 .59 1;"
    fill="freeze" 
    id="circ-anim"/>

وهنا تتابع العرض الحي:

إذا كنت ترغب فقط في تحديد دوال التسارع (easing function) بأكملها دون أي قيم وسيطة، فلا يزال يتعين عليك تحديد الإطارات المفتاحية باستخدام خاصية keyTimes، ولكن عليك فقط تحديد إطارات المفاتيح للبداية والنهاية، وهي1 ؛ 0، وليس القيم المتوسطة values.

الحركات المضافة والتراكمية: addictive وaccumulate

في بعض الأحيان، من المفيد تحديد رسم متحرك يبدأ من حيث انتهت الحركة السابقة؛ أو آخر يستخدم المجموع التراكمي للحركات السابقة كقيمة للمتابعة. لذلك، يحتوي SVG على خاصيتين مناسبتين: addictive وaccumulate.

افترض أن لديك عنصرًا تريد أن "يزداد" عرضه، أو خطًا تريد زيادة طوله، أو عنصرًا تريد نقله خطوة بخطوة من موضع إلى آخر، عبر خطوات منفصلة. هذه الميزة مفيدة بشكل خاص للحركة المتكررة.

تمامًا مثل أي حركة أخرى، ستحدد قيم from وto. ومع ذلك، عند تعيين additive إلىsum، ستكون قيمتهما متناسبة مع القيمة الأصلية للخاصية HTML المتحركة.

لذا، بالعودة إلى دائرتنا، يكون الموضع الأولي لـ cx هو 50. عندما تقوم بتعيين from = "0"‎ وto = "100"‎، فإن الصفر إذا كان في الواقع هو 50 الأصلي، و 100 هو في الواقع 50 + 100؛ بمعنى آخر، من الناحية العملية يشبه from = "50" to = "150"‎.

من خلال القيام بذلك، نحصل على النتيجة التالية:

هذا هو كل ما تفعله خاصية additive. إنه تحدد فقط ما إذا كان يجب أن تكون القيم from وto متناسبة مع القيمة الحالية أم لا. تأخذ الخاصية HTML واحدة من قيمتين فقط: sum وreplace. الأخير هو القيمة الافتراضية، وهذا يعني بشكل أساسي أن القيم from وtoسوف تحل محل القيم الحالية /الأصلية، والتي قد تؤدي في النهاية إلى قفزة غريبة قبل بدء الحركة. (حاول استبدال sum بـreplace في المثال أعلاه للحصول على مقارنة أفضل).

ومع ذلك، ماذا لو أردنا إضافة القيم بحيث يبدأ التكرار الثاني من قيمة نهاية القيمة السابقة؟ هذا هو المكان الذي تأتي فيه خاصية accumulate.

تتحكم خاصية accumulate فيما إذا كانت الحركة تراكمية أم لا. القيمة الافتراضية هي none، مما يعني أنه عند تكرار الحركة على سبيل المثال، ستبدأ من البداية. ومع ذلك، يمكنك تعيينها علىsum، والتي تحدد أن كل إعادة للتكرار بعد الأول يبني على القيمة الأخيرة للتكرار السابق.

لذلك، إذا أردنا العودة إلى الحركة السابقة وتحديد accumulate = "sum"‎، فسنحصل على النتيجة المفضلة التالية:

لاحظ أنه يتم تجاهل خاصية accumulate إذا كانت قيمة الخاصية HTML الهدف لا تدعم الإضافة، أو إذا كان العنصر المتحرك لا يتكرر. سيتم تجاهله أيضًا إذا عُينت وظيفة الحركة مع خاصية to فقط.

تحديد نهاية الحركة باستخدام end

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

فمثلًا في العرض الحي التالي تتحرك الدائرة ذات اللون البرتقالي ببطء في دورة تستمر 30 ثانية بالاتجاه الآخر لمسطح العمل. الدائرة ذات اللون الأخضر ستتحرك فقط عند النقر عليها. حركة الدائرة البرتقالية ستنتهي عند بدء حركة الدائرة الخضراء. انقر على الدائرة الخضراء وانظر كيف ستتوقف حركة الدائرة البرتقالية:

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

تعيين فترات الحركة باستخدام قيم كلًا من begin وend

في الواقع، تقبل كل من الخاصية begin وend قائمة القيم شبه المفصولة. ستتوافق كل قيمة في خاصية begin مع قيمة في خاصية end، وبالتالي تتشكل فواصل متحركة نشطة وغير نشطة.

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

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

لاحظ أنه في المثال أعلاه، استخدمت عنصر<animateTransform> لتدوير المستطيل حول مركزه. سنتحدث عن هذا العنصر بمزيد من التفصيل في القسم التالي أدناه.

لاحظ أيضًا أنه، حتى إذا قمت بتعيين تكرار repeatCount بشكل غير نهائي indefinite، سيتجاوز من خلال قيم end ولن يتكرر بتلك الصورة النهائية.

تقييد المدة النشطة لعنصر باستخدام min وmax

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

بالنسبة لـ min، يحدد طول الحد الأدنى لقيمة المدة النشطة، ويقاس بالوقت الفعلي للعنصر. يجب أن تكون القيمة أكبر من أو تساوي 0، وهي القيمة الافتراضية ولا تقيد المدة النشطة على الإطلاق.

بالنسبة إلىmax، تحدد قيمة الساعة طول الحد الأقصى لقيمة المدة النشطة، وتُقاس في وقت العنصر النشط. يجب أن تكون القيمة أكبر من 0. القيمة الافتراضية لـ max هي indefinite. وهذا لا يقيد المدة النشطة على الإطلاق.

إذا حددت كل من الخاصيتين min وmax، فيجب أن تكون قيمة max أكبر من أو تساوي قيمة min. وإلا فسيتم تجاهل كلا الخاصيتين.

ولكن ما الذي يحدد المدة النشطة لعنصر ما؟ ذكرنا مدة التكرار من قبل، بالإضافة إلى "المدة البسيطة"، وهي مدة الحركة دون أي تكرار (محدد باستخدام dur)، فكيف تعمل كل هذه الأشياء معًا؟ من الذي يتجاوز ماذا؟ ثم ماذا عن الخاصية end التي سيتم تجاوزها ومن ثم ببساطة تنتهي الحركة؟

الطريقة التي يحدث بها ذلك هي أن المتصفح سيقوم أولاً بحساب المدة النشطة بناءً على قيم dur و repeatCount وrepeatDur وend. بعد ذلك، تعمل المدة المحسوبة مقابل قيم min وmax المحددة. إذا كانت النتيجة ضمن الحدود، تكون قيمة المدة المحسوبة الأولى صحيحة ولن تتغير. وإلا قد تحدث حالتان:

  • إذا كانت المدة الزمنية المحسوبة الأولى أكبر من قيمة max، تعرف المدة النشطة للعنصر بأنها تساوي قيمة max.
  • إذا كانت المدة المحسوبة الأولى أقل من قيمة min، تصبح المدة النشطة للعنصر مساوية للقيمة min ويكون سلوك العنصر كما يلي:
    • إذا كانت مدة التكرار (أو المدة البسيطة إذا لم يتكرر العنصر) للعنصر أكبر من min، يُشغل العنصر بشكل طبيعي لمدة نشطة (min مقيدة).
    • بخلاف ذلك، يعمل العنصر بشكل طبيعي خلال مدة التكرار (أو المدة البسيطة إذا لم يتكرر العنصر) ثم يُجمد أو يختفي عتمادًا على قيمة خاصية fill.

هذا يتركنا مع تساؤل عن طريقة قيام المتصفح بحساب المدة النشطة فعليًا. من أجل الإيجاز، لن نخوض في التفاصيل هنا. ولكن هناك جدول شامل للغاية في المواصفات التي تعرض التكوينات المختلفة لخصائص dur، وrepeatCount، وrepeatDor وend، ثم توضح المدة الفعلية التي ستستند إليها كل مجموعة. يمكنك التحقق من الجدول وقراءة المزيد حول هذا الموضوع في هذا القسم من المواصفات

أخيرًا، إذا حدد عنصر للبدء في الحركة قبل العنصر الآخر (على سبيل المثال مع قيمة إزاحة سالبة بسيطة)، يُقاس الحد الأدنى للمدة من وقت البدء المحسوب وليس بداية الملاحظة. هذا يعني أن قيمة min قد لا يكون لها أي تأثير ملحوظ.

أمثلة على <animate>: تحوير المسارات

إحدى الخاصيات التي يمكن تحريكها في SMIL (ولكن ليس في CSS)هي الخاصية d (اختصار لـ data) الخاصة بـ SVG <path>‎. تحتوي الخاصية d على البيانات التي تحدد الخطوط العريضة للشكل الذي ترسمه. تتكون بيانات المسار من مجموعة من الأوامر والإحداثيات التي تخبر المتصفح عن مكان وكيفية رسم النقاط والأقواس والخطوط التي تشكل المسار النهائي.

يسمح لنا تنشيط هذه الخاصية HTML بتحويل مسارات SVG وإنشاء تأثيرات ربط الأشكال. ولكن لكي تتمكن من تحوير الأشكال، يجب أن يكون لأشكال المسار البداية والنهاية وأي مسار وسطي نفس عدد الرؤوس /النقاط بالضبط، ويجب أن تظهر بنفس الترتيب. إذا لم يتطابق عدد النقاط، فلن تعمل الحركة. والسبب في ذلك هو أن تغيير الشكل يحدث فعليًا عن طريق تحريك الرؤوس، وتحريف مواقعها، لذلك إذا كانت نقطة واحدة مفقودة أو غير متطابقة، فلن تُقرب المسارات.

لتحريك مسارSVG، يمكنك تحديد attributeName لتكون d، ثم اضبط قيم from وto التي تحدد أشكال البداية والنهاية، ويمكنك استخدام خاصية values لتحديد أي قيم وسيطة تريد أن يمر الشكل فيما بينها.

من أجل الإيجاز، لن نخوض في تفاصيل كيفية القيام بذلك هنا. بدلًا من ذلك، يمكنك قراءة هذا المقال الممتاز من تأليف Noah Blon، إذ يشرح كيف قام بتحوير الأشكال لإنشاء حركة التحميل باستخدام <animate>. بإمكانك الاطلاع على العرض الحي للمقال:

وهنا مثال آخر لتحوير الأشكال من إعداد Felix Hornoiu:

يمكنك حتى تحوير قيم المسار المستخدم كـ clipping mask مثال على ذلك من إعداد Heather Buchel:

الحركة على طول المسارات الافتراضية: العنصر <animateMotion>

يعد عنصر<animateMotion> عنصر الحركة المفضل في SMIL. يمكنك استخدامه لنقل عنصر على طول المسار. عن طريق تحديد مسار الحركة باستخدام إحدى الطريقتين اللتين سنتطرق لهما بعد ذلك، ثم لإعداد العنصر بحيث يتحرك على طول هذا المسار.

يقبل عنصر<animateMotion> نفس الخاصيات المذكورة سابقًا، بالإضافة إلى ثلاث خاصيات أخرى: keyPoints، و rotate، وpath. أيضًا، هناك اختلاف واحد فيما يتعلق بخاصية calcMode، حيث تكون القيمة الافتراضية لـ <animateMotion> هي paced، وليست linear.

تحديد مسار الحركة باستخدام خاصية path

تستخدم خاصية path لتحديد مسار الحركة. حيث يُعبر عنها بنفس التنسيق وتُفسر بنفس طريقة الخاصية d في العنصر path. يتمثل تأثير الحركة لمسار الحركة في إضافة مصفوفة تحويل تكميلية إلى مصفوفة التحويل الحالية للكائن المشار إليه والتي تتسبب في ترجمة على طول المحورين x و y لنظام إحداثيات المستخدم الحالي بواسطة قيم X و Y المحسوبة على زمن. بمعنى آخر، يُحسب المسار المحدد بالنسبة إلى الموضع الحالي للعنصر، باستخدام بيانات المسار لتحويل العنصر إلى موضع المسار.

بالعودة إلى دائرتنا، سنقوم بتحريكها على طريق يشبه ما يلي:

04.png

بإمكانك تطبيق تلك الحركة على الدائرة باستخدام الشيفرة التالي:

<animateMotion 
    xlink:href="#circle"
    dur="1s"
    begin="click"
    fill="freeze"
    path="M0,0c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,2.3    c13.5,3.5,26.1,9.6,38.5,16.2c12.3,6.5,21.3,16.8,31.9,25.4c10.8,8.7,21,18.3,31.7,26.9c9.3,7.4,20.9,11.5,31.4,16.7
    c13.7,6.8,26.8,9.7,41.8,9c21.4-1,40.8-3.7,61.3-10.4c10.9-3.5,18.9-11.3,28.5-17.8c5.4-3.7,10.4-6.7,14.8-11.5
    c1.9-2.1,3.7-5.5,6.5-6.5" />

هناك أمر واحد ركزنا عليه هنا: الإحداثيات في بيانات المسار. يبدأ المسار عن طريق تحريك (M) إلى النقطة مع الإحداثيات (0، 0)، قبل أن يبدأ في رسم منحنى (c) إلى نقطة أخرى. من المهم أن نلاحظ أن النقطة (0، 0) هي في الواقع موضع الدائرة، بغض النظر عن مكانها - ليست الزاوية العليا اليسرى من نظام الإحداثيات. كما ذكرنا أعلاه، فإن الإحداثيات في خاصية path تتعلق بالموضع الحالي للعنصر!

نتيجة الشيفرة أعلاه هي:

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

<path fill="none" stroke="#000000" stroke-miterlimit="10" d="M100.4,102.2c3.2-3.4,18.4-0.6,23.4-0.6c5.7,0.1,10.8,0.9,16.3,2.3
    c13.5,3.5,26.1,9.6,38.5,16.2c12.3,6.5,21.3,16.8,31.9,25.4c10.8,8.7,21,18.3,31.7,26.9c9.3,7.4,20.9,11.5,31.4,16.7
    c13.7,6.8,26.8,9.7,41.8,9c21.4-1,40.8-3.7,61.3-10.4c10.9-3.5,18.9-11.3,28.5-17.8c5.4-3.7,10.4-6.7,14.8-11.5
    c1.9-2.1,3.7-5.5,6.5-6.5"/>

نقطة بداية المسار في هذه الحالة هي (100.4، 102.2). إذا أردنا استخدام هذه البيانات كمسار للحركة، ستقفز الدائرة بمقدار 100 وحدة تقريبًا إلى اليمين و102 وحدة تقريبًا لأسفل، ثم تبدأ الحركة على طول المسار بالنسبة للموضع الجديد. لذلك، تأكد من وضع ذلك في الاعتبار عند إعداد مسار الحركة للحركة الخاصة بك.

في حالة استخدامها، تحدد الخاصيات from وby وto وvalues شكلًا على مساحة العمل الحالية التي تمثل مسار الحركة.

تحديد مسار الحركة باستخدام عنصر <mpath>

هناك أيضًا طريقة أخرى يمكنك من خلالها تحديد مسار الحركة. بدلًا من استخدام خاصية HTML المسار النسبي path، يمكنك الرجوع إلى مسار خارجي باستخدام عنصر <mpath> التابع لعنصر <animateMotion>، والذي يشير إلى المسار الخارجي باستخدام الخاصية xlink: href.

<animateMotion xlink:href="#circle" dur="1s" begin="click" fill="freeze">
  <mpath xlink:href="#motionPath" />
</animateMotion>

يمكن تحديد مسار الحركة <path> في أي مكان في المستند؛ يمكن تعريفه حرفيًا فقط داخل عنصر <defs> ولا يُعرض على سطح العمل على الإطلاق. في المثال التالي، المسار ظاهر وواضح لأنه في معظم الحالات قد ترغب في إظهار المسار الذي يتحرك فيه العنصر.

لاحظ أنه، وفقا للمواصفات:

اقتباس

توفر نقاط (x، y) المتنوعة من الشكل مصفوفة تحويل تكميلية على CTM للكائن المشار إليه مما يؤدي إلى ترجمة على طول محاور x و y لنظام إحداثيات المستخدم الحالي بواسطة قيم (x، y) الشكل محسوب مع مرور الوقت. وبالتالي، يُترجم الكائن المشار إليه بمرور الوقت عن طريق إزاحة مسار الحركة بالنسبة إلى أصل نظام إحداثيات المستخدم الحالي. يُطبق التحويل الإضافي على أعلى أي تحويلات بسبب خاصية transform أو أي حركة على هذه الخاصية HTML بسبب عنصر animateTransform في العنصر الهدف.

مرة أخرى، "يتضاعف" موضع الدائرة أو "يتحول" بواسطة الإحداثيات في بيانات المسار.

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

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

تتمثل إحدى طرق الالتفاف حول ذلك في البدء بوضع الدائرة في (0، 0)، بحيث عند استخدام بيانات المسار لتحويلها، ستبدأ وتستمر كما هو متوقع.

هناك طريقة أخرى تتمثل في تطبيق تحويل "يعيد تعيين" إحداثيات الدائرة بحيث تُحسب على الصفر قبل تطبيق المسار.

فيما يلي نسخة معدلة من العرض الحي أعلاه، وذلك باستخدام مسار مغلق وتكرار الحركة بشكل غير نهائي.

تجاوز القواعد لـ <animateMotion>

نظرًا لوجود أكثر من طريقة واحدة لفعل نفس الشيء مع animateMotion، فمن المنطقي أن يكون هناك قواعد تجاوز لتحديد القيم التي تتجاوز القيم الأخرى.

قواعد التجاوز لـ animateMotion كالتالي:

  • فيما يتعلق بتعريف مسار الحركة، يتجاوز عنصر mpath خاصية path، التي تتجاوز values، والتي بدورها تتجاوز from وby وto.
  • فيما يتعلق بتحديد النقاط التي تتوافق مع خاصيات keyTimes، فإن مسارkeyPoints يتخطى path، والذي يتجاوز values، والذي بدوره يتخطى from وby وto.

تحديد اتجاه عنصر على طول مسار الحركة باستخدام rotate

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

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

ثم تبدأ السيارة تتحرك على طول مسار الحركة. لكن… هكذا تبدو الحركة:

اتجاه السيارة ثابت، ولا يتغير لمطابقة مسار الحركة. لتغيير ذلك، سنستخدم خاصية rotate.

تأخذ خاصية rotate إحدى القيم الثلاث:

  • auto: تشير إلى أن الكائن يدور بمرور الوقت بزاوية الاتجاه (أي متجه الظل الاتجاهي) لمسار الحركة.
  • auto-reverse: تشير إلى أن الكائن يدور بمرور الوقت بزاوية الاتجاه (أي متجه الظل الاتجاهي) لمسار الحركة بإضافة 180 درجة.
  • رقم: يشير إلى أن العنصر الهدف يحتوي على تحويل دوران ثابت مطبق عليه، حيث تكون زاوية الدوران هي العدد المحدد من الدرجات.

لإصلاح اتجاه السيارة في المثال أعلاه، سنبدأ بتعيين قيمة الدوران على auto. سننتهي بالنتيجة التالية:

أما في حال أردت تدوير السيارة خارج المسار، قيمة auto-revesre ستتكفل بذلك.

يبدو هذا أفضل، لكن لا تزال لدينا مشكلة واحدة وهي أن السيارة تبدو وكأنها تتحرك للخلف على طول المسار! لتغيير ذلك، نحتاج إلى قلب السيارة على طول محورها y. يمكن القيام بذلك عن طريق تحجيمه بعامل "-1" على طول هذا المحور. لذلك، إذا طبقنا التحويل على g باستخدام معرف السيارة car، فستتحرك السيارة للأمام كما هو متوقع. سيرتبط التحجيم مع الترجمة السابقة التي طبقناها سابقًا.

<g id="car" transform="scale (-1, 1) translate(-234.4, -182.8)">

وبالتالي، ستكون النتيجة كما هي موضحة في العرض:

التحكم في مسافة الحركة على طول مسار الحركة باستخدام keyPoints

توفر خاصية keyPoints القدرة على تحديد التقدم على طول مسار الحركة لكل من قيم keyTimes المحددة. إذا حددت ذلك، فإن keyPoints تؤدي إلى تطبيق keyTimes على القيم الموجودة في keyPoints بدلاً من النقاط المحددة في خاصية values أو النقاط الموجودة في خاصية path.

تأخذ keyPoints قائمة مفصولة بفواصل منقوطة لقيم الفاصلة العائمة بين 0 و 1 وتشير إلى مدى طول مسار الحركة التي يجب أن يتحرك فيها الكائن في الوقت المحدد بواسطة قيمة keyTimes المقابلة. تُحدد الحسابات عن بعد من خلال خوارزميات المتصفح. تقابل كل قيمة تقدم في القائمة قيمة في قائمة خاصية keyTimes. عند تحديد قائمة نقاط KeyPoints، يجب أن يكون هناك بالضبط العديد من القيم في قائمة keyPoints كما هو الحال في قائمة keyTimes.

أحد الأشياء المهمة التي يجب ملاحظتها هنا هو ضبط قيمة calcMode على linear لكي تعمل KeyPoints. يبدو أيضًا أنه يجب أن يعمل بشكل منطقي مع الحركة، إذا كانت النقاط الرئيسية تتحرك ذهابًا وإيابًا، لكنها لا تعمل.

فيما يلي مثال من إعداد Amelia Bellamy-Royds (تفقد صفحتها الشخصية) توضح فيه استخدام keyPoints لتقليد السلوك فتبدأ الحركة على مسار من إزاحة محددة مسبقًا، لأننا لا نملك هذه القدرة حاليًا بشكل افتراضي في SMIL.

تحريك النص على طول مسار افتراضي

يختلف نقل النص عبر مسار افتراضي عن نقل عناصر SVG الأخرى عبر المسارات. لتحريك النص، سيتعين عليك استخدام عنصر <animation>، وليس عنصر <animateMotion>.

أولاً، لنبدأ بوضع النص على طول المسار. يمكن القيام بذلك عن طريق تداخل عنصر<textPath> داخل عنصر <text>. سيتحدد النص الذي سيوضع على طول مسار داخل عنصر<textPath>، وليس كتابع لعنصر<text>.

ستقوم textPath بعد ذلك بالإشارة إلى المسار الفعلي المراد استخدامه، تمامًا كما فعلنا في الأمثلة السابقة. يمكن أيضًا تقديم المسار المشار إليه على مسطح العمل أو تعريفه داخل <defs>. تحقق من الشيفرة في العرض الحي التالي:

لتحريك النص على طول هذا المسار، سنستخدم العنصر <animate> لتحريك خاصية startOffset.

تمثل startOffset إزاحة النص على المسار. 0٪ هي بداية المسار؛ 100 ٪ تمثل نهايته. لذلك، على سبيل المثال، إذا ضبطت الإزاحة على 50٪، فسيبدأ النص في منتصف المسار. ومن هنا ستتضح الأمور للذهاب قدمًا.

عن طريق تنشيط startOffset، سنقوم بإنشاء تأثير نقل النص على طول المسار. تحقق من الشيفرة في العرض الحي التالي.

تحولات الحركة: عنصر <animateTransform>

ينقل عنصر <animateTransform> خاصية HTML تحويل على عنصر مستهدف، مما يسمح للحركة بالتحكم في التحوير والتحجيم و/أو التدوير و/أو الربط. يستغرق نفس الخصائص المذكورة للعنصر <animation>، بالإضافة إلى خاصية HTML إضافية: type.

تستخدم خاصية type لتحديد نوع التحويل الذي تُطبق الحركة عليه. وتأخذ أحد القيم الخمسة: translate وscale وrotate وskewX وskewY.

تأخذ الخاصيات from وby وto قيم يعبّرعنها باستخدام تركيب الجملة نفسه المتاح لنوع التحويل المحدد:

  • بالنسبة لـ"type="translate، يُعبر عن كل قيمة فردية كـ [<tx> [,<ty>
  • بالنسبة لـ "type="scale، يُعبر عن كل قيمة فردية كـ [<sx> [,<sy>
  • بالنسبة لـ "type="rotate، يُعبر عن كل قيمة فردية كـ[<rotate-angle> [<cx> <cy>
  • بالنسبة لـ "type="skewX و"type="skewY، يُعبر عن كل قيمة فردية كـ <skew-angle>

إذا لم تكن معتادًا على بناء جملة SVG لخاصية transform، ومن أجل إيجاز هذه المقالة، ولأن تفاصيل بناء الجملة وكيفية التعامل معها خارج نطاقها، بإمكانك قراءة مقال فهم أنظمة تنسيق SVG والتحولات (الجزء 2): خاصية HTML التحويل، قبل الانتقال إلى هذا الدليل.

بالعودة إلى العرض الحي السابق، حيث قمنا بتدوير المستطيل الوردي باستخدام عنصر <animateTransform>. تبدو شيفرة الدوران كما يلي:

<rect id="deepPink-rectangle" width="50" height="50" x="50" y="50" fill="deepPink" />

  <animateTransform 
      xlink:href="#deepPink-rectangle"
      attributeName="transform" 
      attributeType="XML"
      type="rotate"
      from="0 75 75"
      to="360 75 75" 
      dur="2s"
      begin="0s"
      repeatCount="indefinite"
      fill="freeze" 
     />

تحدد خاصيات from وto زاوية الدوران (البداية والنهاية) ومركز الدوران. في كليهما، يبقى مركز الدوران كما هو بالطبع. إذا لم تُحدد المركز، فسيكون ذلك هو الركن العلوي الأيسر من سطح العمل SVG. العرض الحي للرمز أعلاه هو ما يلي:

هنا مثال آخر ممتع لـ animateTransform من إعداد Gabriel:

إن تنشيط عملية تحويل واحدة أمر بسيط، ومع ذلك، يمكن أن تصبح الأمور فوضوية ومعقدة حقًا عند تضمين تحويلات متعددة، خاصة لأن أحد تحويلات الحركة animateTransform يمكنه تجاوز الآخر، لذلك بدلاً من إضافة التأثيرات وتسلسلها، قد ينتهي بك الأمر على العكس تمامًا. هذا بالإضافة إلى طريقة تنسيق SVG للأنظمة والتحولات في الواقع (راجع المقالة المذكورة سابقًا حول الموضوع). الأمثلة واسعة، وخارج نطاق هذه المقالة. لتحويل SVGs، يوصى باستخدام تحويلات CSS. تعمل التطبيقات على جعل هذا الأخير يعمل بشكل مثالي مع SVG، لذلك قد لا تضطر أبدًا إلى استخدام SMIL لتنشيط التحويلات في SVG على الإطلاق.

عنصر <set>

يوفر عنصر set وسيلة بسيطة لتعيين قيمة الخاصية HTML لمدة محددة. يدعم جميع أنواع الخاصيات، بما في ذلك الأنواع التي لا يمكن تحريفها بشكل معقول، مثل قيم السلسلة والقيمة المنطقية. عنصر set غير مضاف ولا يُسمح بالخاصيات المضافة والإجمالية، وسيتم تجاهلها عند تحديدها.

نظرًا لاستخدام <set> لتعيين عنصر إلى قيمة محددة في وقت معين، فإنه لا يقبل جميع الخاصيات المذكورة لعناصر التحريك السابقة. على سبيل المثال، لا يحتوي على خاصية from أو by، لأن القيمة التي تتغير لا تتغير تدريجيًا خلال الفترة الزمنية.

بالنسبة لـ set، يمكنك تحديد العنصر الذي تستهدفه، واسم الخاصية HTML ونوعها، وقيمة to، ويمكن التحكم في توقيت الحركة: begin وdur وend وmin وmax وrestart وrepeatCount وrepeatDur وfill.

فيما يلي مثال يضبط لون المستطيل الدوّار إلى اللون الأزرق عند النقر فوقه. يظل اللون أزرق لمدة 3 ثوانٍ، ثم يعود إلى اللون الأصلي. في كل مرة ينفر فوق المستطيل، تعمل حركة set، ويتغير اللون لمدة ثلاث ثوانٍ.

العناصر وخاصيات HTML وخاصيات الكائنات التي يمكن تحريكها

لا يمكن تحريك كل خاصيات SVG، ولا يمكن تحريك كل الخاصيات التي يمكن تحريكها باستخدام جميع عناصر التحريك. للحصول على قائمة كاملة بجميع الخاصيات المتحركة، وجدول يربط الخاصيات بالعناصر التي تحركها، يرجى الرجوع إلى هذا القسم من مواصفات SVG Animation.

أخيرًا

لدى SMIL الكثير من الإمكانيات، وبالكاد تعرض المقال لمعلومات سطحية جدًا ولم يتطرق إلا إلى الأساسيات والتقنيات الخاصة بكيفية عملها فيSVG. يمكن إنشاء الكثير من التأثيرات المؤثرة للغاية، خاصة تلك التي تنطوي على تحوير الأشكال وتحويلها. لتكن حدودك هي السماء؛ عليك أن تكمل أنت الآن فامض بشغف! ولا تنس مشاركة ما تصنعه مع الآخرين. شكرًا لقراءتك!

ترجمة وبتصرف للمقال A Guide to SVG Animations (SMIL)‎ لصاحبته Sara Soueidan


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...