منع مشاكل الاقتصاص (وغيرها) في عمليات انتقال العرض باستخدام مجموعات انتقال العرض المتداخلة

تاريخ النشر: 22 سبتمبر 2025

عند بدء انتقال عرض، يأخذ المتصفّح تلقائيًا لقطات قبل وبعد للعناصر التي تم وضع علامة view-transition-name عليها. يتم عرض هذه اللقطات في شجرة من العناصر الزائفة. يكون التصميم التلقائي للشجرة "مسطّحًا". وهذا يعني أنّه يتم فقدان التسلسل الهرمي الأصلي في DOM، وتصبح جميع مجموعات انتقالات العرض التي تم التقاطها عناصر شقيقة ضمن عنصر زائف واحد ::view-transition.

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

  • القص (overflow وclip-path وborder-radius): يؤثر القص في العناصر الفرعية للعنصر، ما يعني أنّه لا يمكن للعناصر الشقيقة في مجموعة انتقالات العرض أن تقص بعضها البعض.
  • opacity وmask-image وfilter: وبالمثل، تم تصميم هذه التأثيرات للعمل على صورة نقطية كاملة لشجرة، ما يؤثر في العناصر الفرعية بدلاً من التأثير في كل عنصر على حدة.
  • عمليات التحويل الثلاثية الأبعاد (transform-style وtransform وperspective): لعرض النطاق الكامل لرسومات عمليات التحويل الثلاثية الأبعاد، يجب الحفاظ على بعض التسلسل الهرمي.

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

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

مجموعات انتقالات العرض المتداخلة هي إضافة إلى انتقالات العرض تتيح لك تضمين عناصر ::view-transition-group زائفة داخل بعضها البعض. عندما تكون مجموعات انتقالات العرض متداخلة، يمكن استعادة تأثيرات مثل القص أثناء الانتقال.

Browser Support

  • Chrome: 140.
  • Edge: not supported.
  • Firefox: not supported.
  • Safari: not supported.

من شجرة زائفة مسطّحة إلى شجرة زائفة متداخلة

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

عرض توضيحي مباشر

تسجيل العرض التوضيحي

تسجيل العرض التوضيحي (بسرعة أبطأ)

إذا نظرت عن كثب إلى العرض التوضيحي، ستلاحظ مشكلة في الانتقال: على الرغم من أنّ الفقرات التي تتضمّن الوصف هي عناصر فرعية من العنصر <dialog> في نموذج المستند (DOM)، لا يتم اقتطاع النص بواسطة مربّع <dialog> أثناء الانتقال:

<dialog id="info_bramus" closedby="any">
  <h2><img alt="…" class="avatar" height="96" width="96" src="avatar_bramus.jpg"> <span>Bramus</span></h2>
  <p>Bramus is …</p>
  <p>…</p>
</dialog>

لن يؤدي تطبيق overflow: clip على <dialog> أيضًا إلى تنفيذ أي إجراء.

تكمن المشكلة في طريقة إنشاء عمليات الانتقال بين طرق العرض وعرض شجرتها الوهمية:

  • في الشجرة الوهمية، تكون جميع اللقطات تلقائيًا عناصر شقيقة لبعضها البعض.
  • يتم عرض الشجرة الزائفة في عنصر زائف ::view-transition يتم عرضه فوق المستند بأكمله.

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

html
  ├─ ::view-transition
  │  ├─ ::view-transition-group(card)
  │  │  └─ ::view-transition-image-pair(card)
  │  │     ├─ ::view-transition-old(card)
  │  │     └─ ::view-transition-new(card)
  │  ├─ ::view-transition-group(name)
  │  │  └─ ::view-transition-image-pair(name)
  │  │     ├─ ::view-transition-old(name)
  │  │     └─ ::view-transition-new(name)
  │  ├─ ::view-transition-group(avatar)
  │  │  └─ ::view-transition-image-pair(avatar)
  │  │     ├─ ::view-transition-old(avatar)
  │  │     └─ ::view-transition-new(avatar)
  │  ├─ ::view-transition-group(paragraph1.text)
  │  │  └─ ::view-transition-image-pair(paragraph1.text)
  │  │     └─ ::view-transition-new(paragraph1.text)
  │  └─ ::view-transition-group(paragraph2.text)
  │     └─ ::view-transition-image-pair(paragraph2.text)
  │        └─ ::view-transition-new(paragraph2.text)
  ├─ head
  └─ body
        └─ …

بما أنّ عناصر ::view-transition-group(.text) الوهمية هي عناصر شقيقة تلي عنصر ::view-transition-group(card) الوهمي، يتم رسمها فوق البطاقة.

لكي يكون لديك مقطع ::view-transition-group(card) ::view-transition-group(.text)، يجب أن تكون عناصر ::view-transition-group(.text) الوهمية عناصر فرعية من ::view-transition-group(card). لإجراء ذلك، استخدِم view-transition-group الذي يتيح لك تعيين "مجموعة رئيسية" لعنصر ::view-transition-group() زائف تم إنشاؤه.

لتغيير المجموعة الرئيسية، لديك خياران:

  • في العنصر الرئيسي، اضبط قيمة view-transition-group على contain، لكي يحتوي على جميع العناصر الثانوية التي تتضمّن view-transition-name.
  • اضبط قيمة view-transition-group على view-transition-name للعنصر الرئيسي في جميع العناصر الفرعية. يمكنك أيضًا استخدام nearest لاستهداف مجموعة العنصر الأصل الأقرب.

لذلك، في هذا العرض التوضيحي، لاستخدام مجموعات انتقالات العرض المتداخلة، يصبح الرمز البرمجي كما يلي:

button.clicked,
dialog {
  view-transition-group: contain;
}

أو

button.clicked,
dialog *,
  view-transition-group: nearest;
}

بعد إضافة هذا الرمز، سيتم تضمين عناصر ::view-transition-group(.text) الوهمية داخل عنصر ::view-transition-group(card) الوهمي. يتم ذلك في ::view-transition-group-children(…) pseudo إضافي، ما يحافظ على جميع pseudos المتداخلة معًا:

html
  ├─ ::view-transition
  │  ├─ ::view-transition-group(card)
  │  │  ├─ ::view-transition-image-pair(card)
  │  │  │  ├─ ::view-transition-old(card)
  │  │  │  └─ ::view-transition-new(card)
  │  │  └─::view-transition-group-children(card)
  │  │    ├─ ::view-transition-group(paragraph1.text)
  │  │    │  └─ ::view-transition-image-pair(paragraph1.text)
  │  │    │     └─ ::view-transition-new(paragraph1.text)
  │  │    └─ ::view-transition-group(paragraph2.text)
  │  │       └─ ::view-transition-image-pair(paragraph2.text)
  │  │          └─ ::view-transition-new(paragraph2.text)
  │  ├─ ::view-transition-group(name)
  │  │  └─ ::view-transition-image-pair(name)
  │  │     ├─ ::view-transition-old(name)
  │  │     └─ ::view-transition-new(name)
  │  └─ ::view-transition-group(avatar)
  │     └─ ::view-transition-image-pair(avatar)
  │        ├─ ::view-transition-old(avatar)
  │        └─ ::view-transition-new(avatar)
  ├─ head
  └─ body
        └─ …

أخيرًا، لجعل ::view-transition-group(card) يقتطع الفقرات، طبِّق overflow: clip على ::view-transition-group-children(card):

::view-transition-group-children(card) {
  overflow: clip;
}

والنتيجة هي:

عرض توضيحي مباشر

تسجيل العرض التوضيحي

تسجيل العرض التوضيحي (بسرعة أبطأ)

لا يظهر المعرّف الزائف ::view-transition-group-children إلا عند استخدام مجموعات متداخلة. يتم تحديد حجمها وفقًا لـ border-box الخاص بالعنصر الأصلي، ويتم منحها حدًا شفافًا بالشكل نفسه وسمك الحد نفسه للعنصر الذي أنشأ العنصر الزائف، أي card في المثال السابق.

القصاصات والمزيد

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

html:active-view-transition-type(open) {
    &::view-transition-old(card) {
        animation-name: rotate-out;
    }
    &::view-transition-new(card) {
        animation-name: rotate-in;
    }
}
html:active-view-transition-type(close) {
    &::view-transition-old(card) {
        animation-name: rotate-in;
    }
    &::view-transition-new(card) {
        animation-name: rotate-out;
    }
}

بدون مجموعات انتقالات العرض المتداخلة، لا تدور الصورة الرمزية والاسم مع البطاقة.

عرض توضيحي مباشر

تسجيل العرض التوضيحي

تسجيل العرض التوضيحي (بسرعة أبطأ)

من خلال تضمين العناصر الزائفة الخاصة بالصورة الرمزية والاسم داخل البطاقة، يمكن استعادة التأثير الثلاثي الأبعاد. ولكن هذا ليس كل ما عليك فعله، فبالإضافة إلى تدوير المعرّفَين الزائفَين ::view-transition-old(card) و::view-transition-new(card)، عليك أيضًا تدوير المعرّف الزائف ::view-transition-group-children(card).

html:active-view-transition-type(open) {
    &::view-transition-group-children(card) {
        animation: rotate-in var(--duration) ease;
        backface-visibility: hidden;
    }
}
html:active-view-transition-type(close) {
    &::view-transition-group-children(card) {
        animation: rotate-out var(--duration) ease;
        backface-visibility: hidden;
    }
}

عرض توضيحي مباشر

تسجيل العرض التوضيحي

تسجيل العرض التوضيحي (بسرعة أبطأ)

المزيد من العروض التوضيحية

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

عرض توضيحي مباشر

تسجيل العرض التوضيحي

الأمر المثير للاهتمام في هذا العرض التوضيحي هو أنّ كل العناصر الزائفة ::view-transition-group(.card) يتم تضمينها داخل العنصر الزائف ::view-transition-group(cards) الأصل ويتم قصّها بواسطة هذا العنصر. تم استبعاد #targeted-card لأنّه يجب ألا يتم اقتطاع حركة الدخول/الخروج بواسطة ::view-transition-group(cards).

/* The .cards wrapper contains all children */
.cards {
  view-transition-name: cards;
  view-transition-group: contain;
}

/* Contents that bleed out get clipped */
&::view-transition-group-children(cards) {
  overflow: clip;
}

/* Each card is given a v-t-name and v-t-class */
.card {
  view-transition-name: match-element;
  view-transition-class: card;
}

/* The targeted card is given a unique name (to style the pseudo differently)
   and shouldn't be contained by the ::view-transition-group-children(cards) pseudo */
#targeted-card {
  view-transition-name: targeted-card;
  view-transition-group: none;
}

ملخّص

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

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