هنگامی که یک انتقال دید بین دو سند مختلف اتفاق می افتد، به آن انتقال نمای متقاطع می گویند. این معمولاً در برنامه های چند صفحه ای (MPA) وجود دارد. انتقالهای نمای متقابل اسناد در Chrome از Chrome 126 پشتیبانی میشوند.
پشتیبانی مرورگر
انتقالهای نمای متقاطع بر اساس همان بلوکهای ساختمانی و اصولی مانند انتقالهای نمای همان سند است که بسیار عمدی است:
- مرورگر از عناصری که دارای یک
view-transition-name
منحصر به فرد در هر دو صفحه قدیمی و جدید هستند، عکس فوری می گیرد. - در حالی که رندر متوقف می شود، DOM به روز می شود.
- و در نهایت، انتقال ها توسط انیمیشن های CSS طراحی شده اند.
چیزی که در مقایسه با انتقال نمای سند مشابه متفاوت است، این است که با انتقال نمای متقابل اسناد، برای شروع انتقال دید، نیازی به فراخوانی document.startViewTransition
ندارید. درعوض، محرک انتقال نمای متقابل اسناد، پیمایش با مبدأ یکسان از یک صفحه به صفحه دیگر است، عملی که معمولاً توسط کاربر وبسایت شما با کلیک بر روی پیوند انجام میشود.
به عبارت دیگر، هیچ API برای فراخوانی برای شروع انتقال view بین دو سند وجود ندارد. با این حال، دو شرط وجود دارد که باید رعایت شود:
- هر دو سند باید در یک مبدا وجود داشته باشند.
- هر دو صفحه باید شرکت کنند تا امکان انتقال نمایش فراهم شود.
هر دو این شرایط بعداً در این سند توضیح داده شده است.
انتقالهای نمای متقابل اسناد به پیمایشهای با مبدا یکسان محدود میشود
انتقالهای نمای متقابل اسناد فقط به پیمایشهای همان مبدأ محدود میشود. اگر مبدأ هر دو صفحه شرکت کننده یکسان باشد، ناوبری به عنوان یک منبع در نظر گرفته می شود.
منشا یک صفحه ترکیبی از طرح مورد استفاده، نام میزبان و پورت است که در web.dev توضیح داده شده است .
بهعنوان مثال، میتوانید هنگام پیمایش از developer.chrome.com
به developer.chrome.com/blog
، یک انتقال نمای متقابل اسناد داشته باشید، زیرا آنها یک منبع هستند. هنگام پیمایش از developer.chrome.com
به www.chrome.com
نمیتوانید این انتقال را داشته باشید، زیرا این موارد دارای منبع متقاطع و یک سایت هستند.
انتقالهای نمای متقابل اسناد انتخابی هستند
برای داشتن یک انتقال نمای متقاطع بین دو سند، هر دو صفحه شرکتکننده باید این امکان را انتخاب کنند. این کار با @view-transition
at-rule در CSS انجام می شود.
در @view-transition
at-rule، توصیفگر navigation
را auto
تنظیم کنید تا انتقالهای مشاهده برای پیمایشهای اسناد متقاطع و با مبدا یکسان فعال شود.
@view-transition {
navigation: auto;
}
با تنظیم توصیفگر navigation
بر روی auto
، اجازه میدهید که انتقال مشاهده برای NavigationType های زیر انجام شود:
-
traverse
-
push
یاreplace
، اگر فعال سازی توسط کاربر از طریق مکانیسم های UI مرورگر آغاز نشده باشد.
پیمایشهایی که از auto
حذف میشوند، برای مثال، پیمایش با استفاده از نوار آدرس URL یا کلیک کردن روی یک نشانک، و همچنین هر شکلی از بارگیری مجدد توسط کاربر یا اسکریپت است.
اگر پیمایش بیش از حد طول بکشد - در مورد کروم بیش از چهار ثانیه - با یک TimeoutError
DOMException
از انتقال view صرفنظر می شود.
نسخه ی نمایشی انتقال نمای متقابل
نسخه ی نمایشی زیر را بررسی کنید که از انتقال نمایش برای ایجاد نسخه نمایشی Stack Navigator استفاده می کند. در اینجا هیچ تماسی برای document.startViewTransition()
وجود ندارد، انتقال view با پیمایش از یک صفحه به صفحه دیگر آغاز می شود.
انتقال نمای متقابل اسناد را سفارشی کنید
برای سفارشی کردن انتقال نمای متقابل اسناد، برخی از ویژگی های پلت فرم وب وجود دارد که می توانید از آنها استفاده کنید.
این ویژگیها بخشی از مشخصات View Transition API نیستند، اما برای استفاده در ارتباط با آن طراحی شدهاند.
رویدادهای pageswap
و pagereveal
برای اینکه بتوانید انتقالهای نمای متقابل اسناد را سفارشی کنید، مشخصات HTML شامل دو رویداد جدید است که میتوانید استفاده کنید: pageswap
و pagereveal
.
این دو رویداد برای هر پیمایش اسناد متقاطع با منشأ یکسان فعال می شوند، صرف نظر از اینکه انتقال دیدگاه در شرف وقوع است یا خیر. اگر قرار است یک انتقال view بین دو صفحه اتفاق بیفتد، می توانید با استفاده از ویژگی viewTransition
در این رویدادها به شی ViewTransition
دسترسی پیدا کنید.
- رویداد
pageswap
قبل از رندر شدن آخرین فریم صفحه فعال می شود. میتوانید از این برای انجام برخی تغییرات لحظه آخری در صفحه خروجی، درست قبل از گرفتن عکسهای فوری قدیمی استفاده کنید. - رویداد
pagereveal
پس از شروع اولیه یا فعالسازی مجدد صفحه، اما قبل از اولین فرصت رندر، روی صفحه فعال میشود. با آن، میتوانید صفحه جدید را قبل از گرفتن عکسهای فوری سفارشی کنید.
به عنوان مثال، میتوانید از این رویدادها برای تنظیم یا تغییر سریع برخی از مقادیر view-transition-name
یا انتقال دادهها از یک سند به سند دیگر با نوشتن و خواندن دادهها از sessionStorage
استفاده کنید تا انتقال view را قبل از اجرای واقعی آن سفارشی کنید.
let lastClickX, lastClickY;
document.addEventListener('click', (event) => {
if (event.target.tagName.toLowerCase() === 'a') return;
lastClickX = event.clientX;
lastClickY = event.clientY;
});
// Write position to storage on old page
window.addEventListener('pageswap', (event) => {
if (event.viewTransition && lastClick) {
sessionStorage.setItem('lastClickX', lastClickX);
sessionStorage.setItem('lastClickY', lastClickY);
}
});
// Read position from storage on new page
window.addEventListener('pagereveal', (event) => {
if (event.viewTransition) {
lastClickX = sessionStorage.getItem('lastClickX');
lastClickY = sessionStorage.getItem('lastClickY');
}
});
اگر می خواهید، می توانید تصمیم بگیرید که از انتقال در هر دو رویداد صرفنظر کنید.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
if (goodReasonToSkipTheViewTransition()) {
e.viewTransition.skipTransition();
}
}
}
شی ViewTransition
در pageswap
و pagereveal
دو شی متفاوت هستند. آنها همچنین با وعده های مختلف به طور متفاوتی رفتار می کنند:
-
pageswap
: هنگامی که سند مخفی می شود، شیء قدیمیViewTransition
حذف می شود. وقتی این اتفاق می افتد،viewTransition.ready
رد می کند وviewTransition.finished
حل می شود. -
pagereveal
: وعدهupdateCallBack
قبلاً در این مرحله حل شده است. می توانید از وعده هایviewTransition.ready
وviewTransition.finished
استفاده کنید.
اطلاعات فعال سازی ناوبری
در رویدادهای pageswap
و pagereveal
، میتوانید بر اساس URLهای صفحات قدیمی و جدید نیز اقدام کنید.
به عنوان مثال، در MPA Stack Navigator نوع انیمیشن مورد استفاده به مسیر پیمایش بستگی دارد:
- هنگام پیمایش از صفحه نمای کلی به یک صفحه جزئیات، محتوای جدید باید از سمت راست به چپ وارد شود.
- هنگام پیمایش از صفحه جزئیات به صفحه نمای کلی، محتوای قدیمی باید از چپ به راست خارج شود.
برای انجام این کار به اطلاعاتی در مورد پیمایشی نیاز دارید که در مورد pageswap
، در شرف وقوع است یا در مورد pagereveal
تازه اتفاق افتاده است.
برای این کار، مرورگرها اکنون می توانند اشیاء NavigationActivation
را که اطلاعات مربوط به ناوبری با مبدأ یکسان را در خود نگه می دارند، نشان دهند. این شیء نوع ناوبری استفاده شده، ورودی های تاریخچه مقصد فعلی و نهایی را همانطور که در navigation.entries()
از Navigation API یافت می شود، نشان می دهد.
در یک صفحه فعال شده، می توانید از طریق navigation.activation
به این شی دسترسی داشته باشید. در رویداد pageswap
، میتوانید از طریق e.activation
به آن دسترسی داشته باشید.
این نسخه ی نمایشی Profiles را بررسی کنید که از اطلاعات NavigationActivation
در رویدادهای pageswap
و pagereveal
استفاده می کند تا مقادیر view-transition-name
روی عناصری که باید در انتقال view شرکت کنند، تنظیم کند.
به این ترتیب، شما مجبور نیستید تک تک آیتم های لیست را با یک view-transition-name
در جلو تزئین کنید. در عوض، این کار بهموقع با استفاده از جاوا اسکریپت اتفاق میافتد، فقط در عناصری که به آن نیاز دارند.
کد به شرح زیر است:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove view-transition-names after snapshots have been taken
// (this to deal with BFCache)
await e.viewTransition.finished;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove names after snapshots have been taken
// so that we're ready for the next navigation
await e.viewTransition.ready;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
کد همچنین با حذف مقادیر view-transition-name
پس از اجرای انتقال view بعد از خود پاک می شود. به این ترتیب صفحه برای پیمایش های متوالی آماده است و همچنین می تواند پیمایش تاریخ را انجام دهد.
برای کمک به این کار، از این تابع ابزار استفاده کنید که به طور موقت view-transition-name
تنظیم می کند.
const setTemporaryViewTransitionNames = async (entries, vtPromise) => {
for (const [$el, name] of entries) {
$el.style.viewTransitionName = name;
}
await vtPromise;
for (const [$el, name] of entries) {
$el.style.viewTransitionName = '';
}
}
اکنون می توان کد قبلی را به صورت زیر ساده کرد:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
// Clean up after the page got replaced
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.finished);
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
// Clean up after the snapshots have been taken
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.ready);
}
}
});
صبر کنید تا محتوا با مسدود کردن رندر بارگیری شود
پشتیبانی مرورگر
در برخی موارد، ممکن است بخواهید اولین رندر یک صفحه را تا زمانی که عنصر خاصی در DOM جدید وجود داشته باشد متوقف کنید. این کار از چشمک زدن جلوگیری می کند و از پایداری حالتی که در آن متحرک هستید اطمینان حاصل می کند.
در <head>
، با استفاده از متا تگ زیر، یک یا چند شناسه عنصر را تعریف کنید که باید قبل از دریافت اولین رندر صفحه وجود داشته باشند.
<link rel="expect" blocking="render" href="#section1">
این متا تگ به این معنی است که عنصر باید در DOM وجود داشته باشد، نه اینکه محتوا باید بارگذاری شود. برای مثال در مورد تصاویر، صرف وجود تگ <img>
با id
مشخص شده در درخت DOM کافی است تا شرط به درستی ارزیابی شود. ممکن است خود تصویر همچنان در حال بارگیری باشد.
قبل از اینکه همه چیز را در مورد مسدود کردن رندر انجام دهید، آگاه باشید که رندر افزایشی یک جنبه اساسی وب است، بنابراین در انتخاب مسدود کردن رندر محتاط باشید. تاثیر مسدود کردن رندر باید به صورت موردی ارزیابی شود. به طور پیش فرض، از استفاده blocking=render
خودداری کنید، مگر اینکه بتوانید به طور فعال تأثیر آن را بر روی کاربران خود با اندازه گیری تأثیر بر Core Web Vitals خود اندازه گیری و اندازه گیری کنید.
مشاهده انواع انتقال در انتقال های نمای متقابل اسناد
انتقالهای نمای متقابل اسناد نیز از انواع انتقال نمایش برای سفارشی کردن انیمیشنها و اینکه کدام عناصر گرفته میشوند، پشتیبانی میکنند.
برای مثال، هنگام رفتن به صفحه بعدی یا قبلی در یک صفحه بندی، ممکن است بخواهید از انیمیشن های مختلف بسته به اینکه به صفحه بالاتر یا صفحه پایین تر از دنباله می روید استفاده کنید.
برای تنظیم این انواع از قبل، انواع را در @view-transition
at-rule اضافه کنید:
@view-transition {
navigation: auto;
types: slide, forwards;
}
برای تنظیم انواع در پرواز، از رویدادهای pageswap
و pagereveal
برای دستکاری مقدار e.viewTransition.types
استفاده کنید.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);
e.viewTransition.types.add(transitionType);
}
});
انواع به طور خودکار از شی ViewTransition
در صفحه قدیمی به شی ViewTransition
صفحه جدید منتقل نمی شوند. باید نوع(های) مورد استفاده حداقل در صفحه جدید را تعیین کنید تا انیمیشن ها طبق انتظار اجرا شوند.
برای پاسخ به این انواع، از انتخابگر شبه کلاس :active-view-transition-type()
به همان روشی که با انتقال نمای همان سند استفاده می شود، استفاده کنید.
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
از آنجایی که انواع فقط برای یک انتقال نمای فعال اعمال می شود، انواع به طور خودکار پس از اتمام یک انتقال نمایش پاک می شوند. به همین دلیل، انواع به خوبی با ویژگی هایی مانند BFCache کار می کنند.
نسخه ی نمایشی
در نسخه نمایشی صفحهبندی زیر، محتویات صفحه بر اساس شماره صفحهای که به آن پیمایش میکنید به جلو یا عقب میچرخند.
نوع انتقال برای استفاده در رویدادهای pagereveal
و pageswap
با نگاه کردن به URL ها و از آن تعیین می شود.
const determineTransitionType = (fromNavigationEntry, toNavigationEntry) => {
const currentURL = new URL(fromNavigationEntry.url);
const destinationURL = new URL(toNavigationEntry.url);
const currentPathname = currentURL.pathname;
const destinationPathname = destinationURL.pathname;
if (currentPathname === destinationPathname) {
return "reload";
} else {
const currentPageIndex = extractPageIndexFromPath(currentPathname);
const destinationPageIndex = extractPageIndexFromPath(destinationPathname);
if (currentPageIndex > destinationPageIndex) {
return 'backwards';
}
if (currentPageIndex < destinationPageIndex) {
return 'forwards';
}
return 'unknown';
}
};
بازخورد
بازخورد توسعه دهندگان همیشه قدردانی می شود. برای اشتراکگذاری، یک مشکل را با پیشنهادات و سوالات با کارگروه CSS در GitHub ثبت کنید . پیشوند مشکل خود را با [css-view-transitions]
وارد کنید. اگر با یک باگ مواجه شدید، به جای آن یک باگ Chromium را ثبت کنید .