با استفاده از گروه‌های انتقال نمای تودرتو، از مشکلات بریده شدن (و بیشتر) در انتقال‌های view جلوگیری کنید

منتشر شده: ۲۲ سپتامبر ۲۰۲۵

وقتی یک گذار نما (view transition) را شروع می‌کنید، مرورگر به طور خودکار از عناصری که با 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

  • کروم: ۱۴۰.
  • لبه: ۱۴۰.
  • فایرفاکس: پشتیبانی نمی‌شود.
  • سافاری: پشتیبانی نمی‌شود.

از یک شبه درخت مسطح تا یک شبه درخت تو در تو

در دموی زیر می‌توانید روی آواتار یک شخص کلیک کنید تا اطلاعات بیشتری در مورد آن شخص مشاهده کنید. انیمیشن‌ها توسط یک انتقال نمای سند یکسان مدیریت می‌شوند که دکمه کلیک شده را به کادر محاوره‌ای تبدیل می‌کند، آواتار و نام را در صفحه نمایش حرکت می‌دهد و پاراگراف‌ها را از کادر محاوره‌ای به بالا یا پایین می‌لغزاند.

نسخه آزمایشی زنده

ضبط نسخه آزمایشی

ضبط نسخه آزمایشی (با سرعت کم)

اگر با دقت به دمو نگاه کنید، متوجه خواهید شد که مشکلی در انتقال وجود دارد: اگرچه پاراگراف‌های حاوی توضیحات، فرزندان عنصر <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 رندر می‌شود که روی کل سند رندر می‌شود.

برای این نسخه آزمایشی، درخت DOM به شکل زیر است:

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) clip ::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(…) انجام می‌شود که همه شبه‌کدهای تو در تو را در کنار هم نگه می‌دارد:

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 فقط زمانی وجود دارد که از گروه‌های تو در تو استفاده شود. اندازه‌ی آن به اندازه‌ی کادر حاشیه‌ی عنصر اصلی است و یک حاشیه‌ی شفاف با همان شکل و ضخامت حاشیه‌ی عنصری که شبه‌جمله را تولید کرده است - 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 را حفظ کنید. این امر، جلوه‌های متنوعی را که قبلاً با انتقال‌های نما امکان‌پذیر نبودند، فعال می‌کند که برخی از آنها را در اینجا شرح داده‌ایم.

تودرتوسازی، مدل نحوه ساخت انتقال‌های نما را تغییر می‌دهد و قرار است برای ایجاد جلوه‌های پیشرفته مورد استفاده قرار گیرد. همانطور که اشاره شد، انتقال‌های نمای مبتنی بر عنصر همچنین می‌توانند زیرمجموعه‌ای از جلوه‌ها را با یک مدل ساده‌تر انجام دهند. ما شما را تشویق می‌کنیم که هر دو ویژگی را امتحان کنید تا تصمیم بگیرید کدام یک به بهترین وجه با نیازهای شما مطابقت دارد.