AMP

amp-animation

Description

Defines and displays an animation.

 

Required Scripts

<script async custom-element="amp-animation" src="https://cdn.ampproject.org/v0/amp-animation-0.1.js"></script>

Supported Layouts

يحدد المكّوِن الحركات ويشغّلها.

النص البرمجي المطلوب <script async custom-element="amp-animation" src="https://cdn.ampproject.org/v0/amp-animation-0.1.js"></script>
التنسيقات المعتمدة nodisplay
أمثلة animations.amp.html

[جدول المحتويات]

نظرة عامة

تعتمد "حركات AMP" على واجهة برمجة تطبيقات Web Animations لتحديد الحركات وتشغيلها في مستندات AMP.

التنسيق

يحدد عنصر amp-animation الحركة كبنية JSON.

مواصفات حركات المستوى الأعلى

يحدد كائن المستوى الأعلى عملية الحركة الكلية التي تتكون من عدد عشوائي من مكونات الحركة التي يتم تحديدها كمصفوفة animations:

<amp-animation layout="nodisplay">
<script type="application/json">
{
  // Timing properties
  ...
  "animations": [
    {
      // Animation 1
    },
    ...
    {
      // Animation N
    }
  ]
}
</script>

الوضع في DOM

يُسمح بوضع المكّوِن <amp-animation> فقط كعنصر ثانوي مباشر للعنصر <body> إذا كانت السمة trigger="visibility". إذا لم يتم تحديد trigger وتم التحكم في تشغيل الحركة برمجيًا من خلال إجراءاتها، يمكن عندها وضع المكوِّن في أي مكان في DOM.

مكوِّن الحركة

كل مكوِّن من مكونات الحركة عبارة عن تأثير إطارات رئيسية يتألف من:

  • العناصر الهدف المُشَار إليها بواسطة محدد
  • الشروط: الاستعلام عن الوسائط والشرط supports
  • خصائص التوقيت
  • الإطارات الرئيسية
{
  "selector": "#target-id",
  // Conditions
  // Variables
  // Timing properties
  // Subtargets
  ...
  "keyframes": []
}

الشروط

يمكن أن تحدد الشروط ما إذا سيتم تضمين مكون الحركة في الحركة النهائية أو لا.

الاستعلام عن الوسائط

يمكن تحديد الاستعلام عن الوسائط باستخدام الخاصية media. يمكن أن تحتوي هذه الخاصية على أي تعبير مسموح به لواجهة برمجة تطبيقات Window.matchMedia ويتوافق مع قاعدة @media في CSS.

إذا تم تحديد قيمة لأحد مكونات الحركة، لن يتم تضمين المكوِّن إلا إذا تطابق الاستعلام عن الوسائط مع البيئة الحالية.

الشرط Supports

يمكن تحديد الشرط Supports باستخدام الخاصية supports. يمكن أن تحتوي هذه الخاصية على أي تعبير مسموح به لواجهة برمجة تطبيقات CSS.supports ويتوافق مع قاعدة @supports في CSS.

إذا تم تحديد قيمة لأحد مكونات الحركة، لن يتم تضمين المكوِّن إلا إذا تطابق الشرط supports مع البيئة الحالية.

عبارة الحركة switch

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

{
  {
    // Optional selector, vars, timing
    ...
    "switch": [
      {
        "media": "(min-width: 320px)",
        "keyframes": {...},
      },
      {
        "supports": "offset-distance: 0",
        "keyframes": {...},
      },
      {
        // Optional default: no conditionals
      }
    ]
  }

في الحركة switch، يتم تقييم الحركات المرشحة بالترتيب المحدد، ويتم تنفيذ أول حركة تتطابق مع العبارات الشرطية وتجاهل البقية.

على سبيل المثال، تشغِّل هذه الحركة حركة motion-path إذا كانت متاحة، وترجع إلى التحويل:

{
  "selector": "#target1",
  "duration": "1s",
  "switch": [
    {
      "supports": "offset-distance: 0",
      "keyframes": {
        "offsetDistance": [0, '300px']
      }
    },
    {
      "keyframes": {
        "transform": [0, '300px']
      }
    }
  ]
}

المتغيرات

يمكن لمكوِّن الحركة أن يعلن عن متغيرات CSS التي سيتم استخدامها لقيم التوقيت والإطارات الرئيسية من خلال التعبيرات var(). ويتم تقييم التعبيرات var() باستخدام السياق الهدف الحالي. يتم نشر متغيرات CSS المحددة في مكونات الحركة إلى حركات مدمجة، ويتم تطبيقها على أهداف الحركة وبالتالي تُلغي CSS المستخدَمة في الحركات النهائية.

على سبيل المثال:

<amp-animation layout="nodisplay">
<script type="application/json">
{
  "--delay": "0.5s",
  "--x": "100px",
  "animations": [
    {
      "selector": "#target1",
      "delay": "var(--delay)",
      "--x": "150px",
      "keyframes": {"transform": "translate(var(--x), var(--y, 0px)"}
    },
    ...
  ]
}
</script>
</amp-animation>

في هذا المثال:

  • يتم نشر --delay في الحركات المدمجة وتستخدَم كوقت فاصل للحركة #target1.
  • يتم نشر --x في الحركات المدمجة ولكن ألغتها الحركة #target1 وتم استخدامها لاحقًا للخاصية transform.
  • لم يتم تحديد --y في أي مكان في <amp-animation> وبالتالي سيتم الاستعلام عنه في العنصر #target1. ويتم تلقائيًا تعيين القيمة 0px في حال عدم تحديده في CSS أيضًا.

راجع قسم var() وcalc() للحصول على المزيد من المعلومات عن var().

خصائص التوقيت

قد تحتوي مكونات حركة المستوى الأعلى ومكونات الحركة على خصائص التوقيت. يتم تعريف هذه الخصائص بالتفصيل في AnimationEffectTimingProperties من مواصفات Web Animation. وتتضمن مجموعة الخصائص المسموح بها هنا:

الخاصية النوع القيمة التلقائية الوصف
duration وقت 0 تمثل الخاصية مدة الحركة. وإما أن تكون قيمة رقمية بالملي ثانية أو قيمة وقت CSS، مثل `2s`.
delay وقت 0 تمثل الوقت الفاصل قبل بدء تنفيذ الحركة. وإما أن تكون قيمة رقمية بالملي ثانية أو قيمة وقت CSS، مثل `2s`.
endDelay وقت 0 تمثل الوقت الفاصل بعد اكتمال الحركة وقبل اعتبارها مكتملة فعليًا. وإما أن تكون قيمة رقمية بالملي ثانية أو قيمة وقت CSS، مثل `2s`.
iterations رقم أو
"Infinity" أو
"infinite"
1 عدد مرات تكرار تأثير الحركة.
iterationStart رقم/CSS 0 معادلة الوقت التي يبدأ فيها تأثير الحركة.
easing سلسلة "linear" دالة التوقيت التي تستخدَم لقياس الوقت لتغيير سرعة التأثيرات.
direction سلسلة "normal" إحدى القيم "normal" أو "reverse" أو "alternate" أو "alternate-reverse".
fill سلسلة "none" إحدى القيم "none" أو "forwards" أو "backwards" أو "both" أو "auto".

تسمح كل خصائص التوقيت إما بقيم رقمية/سلسلة مباشرة أو قيم CSS. يمكن مثلاً تحديد "duration" بالقيمة 1000 أو 1s أو 1000ms. بالإضافة إلى ذلك، يُسمح أيضًا باستخدام calc() وvar() وتعبيرات CSS الأخرى.

مثال لخصائص التوقيت بالترميز JSON:

{
  ...
  "duration": "1s",
  "delay": 100,
  "endDelay": "var(--end-delay, 10ms)",
  "easing": "ease-in",
  "fill": "both"
  ...
}

تكتسب مكونات الحركة خصائص التوقيت المحددة لحركة المستوى الأعلى.

الأهداف الفرعية

متى توفرت إمكانية تحديد selector، فمن الممكن أيضًا تحديد subtargets: []. يمكن أن تلغي الأهداف الفرعية خصائص التوقيت أو المتغيرات المحددة في الحركة لأهداف فرعية معينة وموضّحة إما من خلال فهرس أو محدد CSS.

على سبيل المثال:

{
  "selector": ".target",
  "delay": 100,
  "--y": "100px",
  "subtargets": [
    {
      "index": 0,
      "delay": 200,
    },
    {
      "selector": ":nth-child(2n+1)",
      "--y": "200px"
    }
  ]
}

في هذا المثال، تم تلقائيًا تعيين الوقت الفاصل لجميع الأهداف المطابقة لـ ".target" بقيمة 100ms ولـ "- y" بقيمة 100px. ومع ذلك، يتم إلغاء الهدف الأول (index: 0) ليكون الوقت الفاصل 200ms بينما تم إلغاء الأهداف الفردية لتكون "--y" بقيمة 200px.

لاحظ إمكانية تطابق عدة أهداف فرعية مع عنصر هدف واحد.

الإطارات الرئيسية

يمكن تحديد الإطارات الرئيسية بعدة طرق موضحة في قسم الإطارات الرئيسية من مواصفات Web Animations أو كسلسلة تشير إلى اسم @keyframes في CSS.

في ما يلي بعض الأمثلة المعتادة لتعريفات الإطارات الرئيسية.

يحدد تنسيق "to" لشكل الكائن المختزَل الحالة النهائية على 100%:

{
  "keyframes": {"opacity": 0, "transform": "scale(2)"}
}

يحدد التنسيق "from-to" لشكل الكائن المختزَل حالتي البداية والنهاية على 0 و100%:

{
  "keyframes": {
    "opacity": [1, 0],
    "transform": ["scale(1)", "scale(2)"]
  }
}

يحدد التنسيق "value-array" لشكل الكائن المختزَل عدة قيم لحالتي البداية والنهاية وعدة معادلات (متساوية التباعد):

{
  "keyframes": {
    "opacity": [1, 0.1, 0],
    "transform": ["scale(1)", "scale(1.1)", "scale(2)"]
  }
}

يحدد شكل المصفوفة الإطارات الرئيسية. ويتم تعيين المعادلات تلقائيًا على 0 و100% مع التوزيع بين القيمتين بتباعد متساوٍ:

{
  "keyframes": [
    {"opacity": 1, "transform": "scale(1)"},
    {"opacity": 0, "transform": "scale(2)"}
  ]
}

يمكن أن يتضمن شكل المصفوفة أيضًا "المعادلة" بشكل صريح:

{
  "keyframes": [
    {"opacity": 1, "transform": "scale(1)"},
    {"offset": 0.1, "opacity": 0.1, "transform": "scale(2)"},
    {"opacity": 0, "transform": "scale(3)"}
  ]
}

يمكن أيضًا أن يتضمن شكل المصفوفة "تغيير السرعة":

{
  "keyframes": [
    {"easing": "ease-out", "opacity": 1, "transform": "scale(1)"},
    {"opacity": 0, "transform": "scale(2)"}
  ]
}

للحصول على مزيد من التنسيقات الإضافية للإطارات الرئيسية، راجع مواصفات Web Animations.

تسمح قيم الخاصية بأي قيم CSS صالحة، بما في ذلك calc() وvar() وغيرها من تعبيرات CSS.

الإطارات الرئيسية من CSS

توجد طريقة أخرى لتحديد الإطارات الرئيسية كقاعدة في ورقة أنماط المستند (العلامة <style>) كقاعدة @keyframes في CSS. على سبيل المثال:

<style amp-custom>
  @keyframes keyframes1 {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
</style>

<amp-animation layout="nodisplay">
<script type="application/json">
{
  "duration": "1s",
  "keyframes": "keyframes1"
}
</script>
</amp-animation>

إطارات CSS @keyframes مساوية تقريبًا لتضمين تعريف الإطارات الرئيسية في JSON وفقًا لمواصفات Web Animations. ومع ذلك، هناك بعض الفروق

  • للحصول على الدعم على مستوى المنصة، قد يلزم توفير بادئات الموردين، مثل @-ms-keyframes {}، أو قد يلزم توفير -moz-transform. لا حاجة إلى بادئات الموردين ولا يُسمح بها في التنسيق JSON، ولكنها قد تكون ضرورية في CSS.
  • لن تتمكن المنصات التي لا تتيح calc() وvar() من الاستفادة من تعويضات amp-animation عند تحديد الإطارات الرئيسية في CSS. ننصح دائمًا بتضمين قيم احتياطية في CSS.
  • يتعذر استخدام إضافات CSS، مثل width() وheight() وnum() وrand() وindex() وlength() في CSS.

الخصائص المدرجة في القائمة البيضاء للإطارات الرئيسية

لا يمكن استخدام جميع خصائص CSS في الإطارات الرئيسية. وحدها خصائص CSS التي يمكن للمتصفحات الحديثة تحسينها وتحريكها بسرعة مدرَجة في القائمة البيضاء. وستزيد هذه القائمة كلما أكدت خصائص إضافية أنها توفر أداء جيدًا. تحتوي القائمة الحالية على: - opacity - transform - visibility - offset-distance

لاحظ أن استخدام خصائص CSS المبدوءة بالموردين ليست ضرورية أو مسموحًا بها.

الأشكال المختصرة لتهيئة الحركات

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

<amp-animation layout="nodisplay">
<script type="application/json">
{
  "selector": "#target-id",
  "duration": "1s",
  "keyframes": {"opacity": 1}
}
</script>
</amp-animation>

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

<amp-animation layout="nodisplay">
<script type="application/json">
[
  {
    "selector": ".target-class",
    "duration": 1000,
    "keyframes": {"opacity": 1}
  },
  {
    "selector": ".target-class",
    "duration": 600,
    "delay": 400,
    "keyframes": {"transform": "scale(2)"}
  }
]
</script>
</amp-animation>

إنشاء الحركة

يمكن أن تشير الحركات إلى حركات أخرى، وبالتالي فهي تجمع بين عدة إعلانات للمكّوِن amp-animation في حركة نهائية واحدة. إن الإشارة إلى حركة من حركة أخرى تساوي تقريبًا عملية الدمج. يكمن سبب تقسيم الحركة إلى عناصر مختلفة في إعادة استخدام الحركة نفسها من عدة أماكن أو تصغير إعلان كل حركة وزيادة إمكانية إدارتها.

على سبيل المثال:

<amp-animation id="anim1" layout="nodisplay">
<script type="application/json">
{
  "animation": "anim2",
  "duration": 1000,
  "--scale": 2
}
</script>
</amp-animation>

<amp-animation id="anim2" layout="nodisplay">
<script type="application/json">
{
  "selector": ".target-class",
  "keyframes": {"transform": "scale(var(--scale))"}
}
</script>
</amp-animation>

تضم الحركة التي في المثال الحركة "anim2" كجزء من "anim1". ويتم تضمين "anim2" بدون هدف (selector). في هذه الحالة، من المتوقع أن تشير الحركة المضمنَّة إلى هدفها الخاص.

يتيح نموذج آخر تضمين الحركة لتوفير الهدف أو أهداف متعددة. في هذه الحالة، يتم تنفيذ الحركة المضمنَّة لكل هدف مطابق. على سبيل المثال:

<amp-animation id="anim1" layout="nodisplay">
<script type="application/json">
{
  "selector": ".target-class",
  "animation": "anim2",
  "duration": 1000,
  "--scale": 2
}
</script>
</amp-animation>

<amp-animation id="anim2" layout="nodisplay">
<script type="application/json">
{
  "keyframes": {"transform": "scale(var(--scale))"}
}
</script>
</amp-animation>

هنا، عندما يتطابق ".target-class" مع عنصر واحد أو عدة عناصر أو لا يحدث تطابق، يتم تنفيذ "anim2" لكل هدف مطابق.

يتم تمرير المتغيرات وخصائص التوقيت المحددة في حركة الطلب إلى الحركة المضمنَّة أيضًا.

تعبيرات var() وcalc()

يتيح amp-animation استخدام تعبيرات var() وcalc() لقيم التوقيت والإطارات الرئيسية.

على سبيل المثال:

<amp-animation layout="nodisplay">
<script type="application/json">
[
  {
    "selector": ".target-class",
    "duration": "4s",
    "delay": "var(--delay)",
    "--y": "var(--other-y, 100px)",
    "keyframes": {"transform": "translate(calc(100vh + 20px), var(--y))"}
  }
]
</script>
</amp-animation>

يتم تعويض كل من var() وcalc() في المنصات التي لا تتيحهما بشكل مباشر. يتم استخراج الخصائص var() من العناصر الهدف المقابلة. ومع ذلك، يتعذر للأسف تعويض var() بشكل كامل. وبالتالي، عندما يكون التوافق مهمًا، ننصح بشدة بتضمين القيم التلقائية في تعبيرات var(). على سبيل المثال:

<amp-animation layout="nodisplay">
<script type="application/json">
[
  {
    "selector": ".target-class",
    "duration": "4s",
    "delay": "var(--delay, 100ms)",
  }
]
</script>
</amp-animation>

يمكن لمكونات الحركة تحديد متغيراتها الخاصة، مثل حقول --var-name. يتم نشر هذه المتغيرات في الحركات المدمجة وتلغي متغيرات العناصر الهدف المحددة من خلال ورقة الأنماط (العلامة <style>). تحاول تعبيرات var() أولاً حل قيم المتغيرات المحددة في الحركات ثم تستعلم عن أنماط الهدف.

إضافات CSS

يوفر amp-animation إضافات CSS متعددة لتلبية احتياجات الحركات المعتادة: rand() وnum() وwidth() وheight(). يمكن استخدام هذه الدوال متى أمكن استخدام قيم CSS ضمن amp-animation، بما في ذلك قيم التوقيت والإطارات الرئيسية.

الإضافة index() من CSS

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

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

{
  "selector": ".class-x",
  "delay": "calc(200ms * index())"
}

الإضافة length() من CSS

تعرض الدالة length() عدد العناصر الهدف في تأثير الحركة. وتكون هذه الإضافة أكثر صلة عند اقترانها بالإضافة index():

{
  "selector": ".class-x",
  "delay": "calc(200ms * (length() - index()))"
}

الإضافة rand() من CSS

تعرض الدالة rand() قيمة CSS عشوائية ولها شكلان.

شكل بدون وسيطات يعرض الرقم العشوائي من 0 و1.

{
delay: "calc(10s * rand())"
}

الشكل الثاني يحتوي على وسيطتين ويعرض القيمة العشوائية بين هاتين الوسيطتين.

{
  "delay": "rand(5s, 10s)"
}

الإضافات width() وheight() من CSS

تعرض إضافتَا width() وheight() عرض/ارتفاع العنصر المتحرك أو العنصر المعيَّن بالمحدد. تكون القيمة المعروضة بالبكسل، مثل 100px.

الأشكال التالية متاحة: - width() وheight() - عرض/ارتفاع العنصر المتحرك. - width('.selector') وheight('.selector') - عرض/ارتفاع العنصر المعيَّن بالمحدد. يمكن استخدام أي محدد CSS مثل width('#container &gt; li'). - width(closest('.selector')) وheight(closest('.selector')) - عرض/ارتفاع العنصر المعيَّن بأقرب محدد.

الإضافتان width() وheight() مفيدتان بشكل خاص للتحويلات. خصائص left وtop وخصائص CSS المشابهة يمكنها استخدام قيم % للتعبير عن الحركات نسبة حجم الحاوية. ومع ذلك، تفهم الخاصية transform قيم % بشكل مختلف، فهي تفهمها كنسبة مئوية من العنصر المحدد. وبالتالي، يمكن استخدام width() وheight() للتعبير عن حركات التحويل من حيث عناصر الحاوية وما شابهها.

يمكن دمج هذه الدالات مع تعبيرات calc() وvar() وغيرها من تعبيرات CSS. على سبيل المثال:

{
  "transform": "translateX(calc(width('#container') + 10px))"
}

الإضافة num() من CSS

تعرض الدالة num() تمثيلاً رقميًا لقيمة CSS. على سبيل المثال: - num(11px) yields 11; - num(110ms) yields 110; - etc.

على سبيل المثال، يحسب التعبير التالي الوقت الفاصل بالثواني نسبة إلى عرض العنصر:

{
  "delay": "calc(1s * num(width()) / 100)"
}

صور SVG المتحركة

صور SVG المتحركة رائعة ونحن بالتأكيد ننصح باستخدامها في الحركات.

تتم إتاحة صور SVG المتحركة عبر نفس خصائص CSS الموضحة في الخصائص المدرجة في القائمة البيضاء للإطارات الرئيسية مع بعض الفروق الدقيقة:

  • عناصر IE/Edge SVG لا تتيح خصائص transform من CSS. يتم تعويض الحركة transform نفسها. ومع ذلك، لا يتم تطبيق الحالة الأولية المحددة في ورقة الأنماط. إذا كانت الحالة الأولية المحوّلة مهمة على IE/Edge، يُنصح بتكرارها عبر السمة SVGtransform.
  • في حين يتم تعويض CSS transform لـ IE/Edge، فمن المستحيل لسوء الحظ تعويض الخاصية transform-origin. وبالتالي، عندما يكون التوافق مع IE/Edge مطلوبًا، يُنصح باستخدام transform-origin التلقائية فقط.
  • تواجه معظم المتصفحات حاليًا مشاكل في فهم CSS transform-origin بشكل صحيح. اطّلِع على مشاكل Chrome وSafari وFirefox. ويُفترَض حل غالبية هذا الالتباس بمجرد تنفيذ CSS transform-box. عندما تكون transform-origin مهمة، يُنصَح أيضًا بتضمين transform-box CSS المطلوبة حفاظًا على التوافق في المستقبل.

تشغيل الحركة

يمكن تشغيل الحركة عبر السمة trigger أو الإجراء on.

السمة trigger

القيمة visibility هي القيمة الوحيدة المتاحة حاليًا للسمة trigger. يتم تشغيل visibility عندما يكون المستند أو التضمين الأساسي مرئيًا (في إطار العرض).

على سبيل المثال:

<amp-animation id="anim1" layout="nodisplay"
    trigger="visibility">
  ...
</amp-animation>

التشغيل عبر الإجراء on

على سبيل المثال:

<amp-animation id="anim1" layout="nodisplay">
  ...
</amp-animation>
<button on="tap:anim1.start">Animate</button>

الإجراء on

يصدِّر العنصر amp-animation الإجراءات التالية:

  • start - لبدء الحركة التي لم تبدأ ويمكن تحديد خصائص التوقيت والمتغيرات كوسيطات للإجراء. مثال: anim1.start(delay=-100, --scale=2)
  • restart - لبدء الحركة أو إعادة تشغيل حركة العاملة حاليًا ويمكن تحديد خصائص التوقيت والمتغيرات كوسيطات للإجراء. مثال: anim1.start(delay=-100, --scale=2)
  • pause - لإيقاف الحركة العاملة حاليًا
  • resume - لاستئناف الحركة العاملة حاليًا
  • togglePause - للتبديل بين إجراء الإيقاف المؤقت وإجراء الاستئناف
  • seekTo - لإيقاف الحركة مؤقتًا والانتقال إلى النقطة الزمنية المحددة في الوسيطة time بالملي ثانية أو الوسيطة percent كنقطة مئوية في المخطط الزمني
  • reverse - لعكس الحركة
  • finish - لإنهاء الحركة
  • cancel - لإلغاء الحركة
هل تحتاج إلى مزيد من المساعدة؟

لقد قرأت هذا المستند عشرات المرات ولكن ألم يُجِب عن جميع أسئلتك؟ ربما حصل ذات الأمر مع أشخاص آخرين: تواصل معهم على Stack Overflow.

الذهاب إلى Stack Overflow
هل وجدت خطأ أو تفتقد لميزة؟

يشجع مشروع AMP مشاركتك ومساهمتك بشدة! ونأمل أن تكون مشاركًا دائمًا في مجتمعنا مفتوح المصدر، ولكننا نشجع أيضًا المساهمات التي تحدث لمرة واحدة في الأمور التي تتحمس لها.

الانتقال إلى GitHub