תאריך פרסום: 28 באוגוסט 2025
לחיפוש Google יש את אחד מטווח ההגעה הגדולים בעולם, ולכן שינויים בחוויית המשתמש שלנו יכולים להשפיע על מיליארדי משתמשים. חלמנו במשך זמן רב על חוויית גלישה באתר שתהיה מודרנית יותר ותזכיר שימוש באפליקציה. כשנכנסנו לפיתוח מצב AI, רצינו ליצור למשתמשים חוויה שבה המעבר מחיפוש רגיל למצב AI יהיה חלק ומשולב. כששמענו על מעברים בין תצוגות של מסמכים שונים, ידענו שזה יהיה שילוב מושלם לתכונה הזו. במקרה לדוגמה הזה אנחנו משתפים את מה שלמדנו כששילבנו את תכונת המעבר לצד השקת מצב AI.
מעברים בין תצוגות במסמכים שונים הם פריצת דרך בכל הנוגע לכלים מובנים בדפדפן, ואנחנו נרגשים לראות איך הם ישפיעו על האינטרנט בעתיד.
שינוי הסטטוס קוו
לחיפוש Google יש דרישות מחמירות ושמרניות לתמיכה בדפדפן. באופן כללי, השימוש בתכונה עם זמינות מוגבלת היה אסור. במעברים בין תצוגות של מסמכים שונים, גילינו שאי אפשר להשתמש ב-polyfill, והסיבה העיקרית לכך היא שלא היה API לצילום תמונות פיקסלים, ושיבוט של אזור התצוגה כולו גרם לבעיות משמעותיות בביצועים. לכן, השימוש בתכונה כשיפור הדרגתי היה הדרך הטובה ביותר להשיק אותה לצד מצב AI. מאחר שהאנימציות שנוצרות על ידי מעברי תצוגה לא משפיעות ישירות על הפונקציונליות של האתר, הן פשוט יושבתו בתנועה שלא נתמכת, וזה כבר המצב הנוכחי בייצור ללא אנימציות מעבר.
קודם כל בדקנו את אסטרטגיית השיפור ההדרגתי הזו עם משתמשים פנימיים. כך קיבלנו משוב מוקדם, שהיה חיובי ברובו. המשוב שקיבלנו גם חשף באגים, כולל בעיות בביצועים ואינטראקציות לא מכוונות עם תכונות אחרות, כמו הקשרים חופפים של שכבות.
האסטרטגיה הזו הוכיחה את עצמה, ואני מאמין שננסה אותה גם עם תכונות חדשות אחרות בדפדפן בעתיד.
קשיים שנתקלנו בהם ופתרונות לבעיות
זמן אחזור, עיבוד החסימה וטיימרים מפקחים
בסך הכול, זמן האחזור הנוסף שנובע ממעברי תצוגה של MPA זניח ב-99% מתרחישי השימוש, במיוחד בחומרה מודרנית. עם זאת, בחיפוש Google יש דרישות גבוהות מאוד בכל הנוגע לזמן האחזור, ואנחנו שואפים ליצור חוויות משתמש שפועלות היטב בכל המכשירים. אפילו כמה אלפיות שנייה נוספות חשובות לנו, ולכן השקענו מאמצים כדי להבין איך להטמיע בצורה נכונה מעברים בין תצוגות במסמכים שונים, בלי לפגוע בחוויית המשתמש של אף אחד.
חסימת עיבוד היא טכניקה שמתאימה במיוחד למעברי תצוגה בין מסמכים. בתמונות המייצגות של פסאודו-אלמנטים במסמך הנכנס אפשר לראות רק תוכן שכבר עבר עיבוד. לכן, כדי להנפיש תוכן ממסמך נכנס, צריך לעבד את הבלוק עד שהאלמנט המיועד להנפשה יעובד. כדי לעשות את זה, צריך להשתמש במאפיין blocking בפריט HTMLLinkElement. לחסימת עיבוד יש חסרונות, כי המתנה לאלמנט שנמצא לקראת סוף עץ ה-DOM של המסמך הנכנס עלולה להשפיע באופן משמעותי על זמן האחזור. היינו צריכים לאזן את הפשרה הזו בהתאם, ולכן אנחנו מעבדים את הבלוק רק ברכיבים שעוברים עיבוד מוקדם מאוד במחזור החיים של הדף.
<!-- Link tag in the <head> of the incoming document -->
<link blocking="render" href="#target-id" rel="expect">
<!-- Element you want to animate in the <body> of the incoming document -->
<div id="target-id">
some content
</div>
במקרים מסוימים, לא מספיק לציין במדויק את הרכיב שבו רוצים להציג את הבלוק. במכשירים או בחיבורים מסוימים עדיין תהיה השהיה מוספת גם כשחוסמים את העיבוד ברכיב שנמצא קרוב לתחילת עץ ה-DOM. כדי לטפל במקרים כאלה, כתבנו סקריפט של טיימר watchdog להסרת התג HTMLLinkElement אחרי שחלף פרק זמן מסוים, כדי לאלץ את ביטול החסימה של עיבוד המסמך הנכנס.
דרך פשוטה לעשות את זה:
function unblockRendering() {
const renderBlockingElements = document.querySelectorAll(
'link[blocking=render]',
);
for (const element of renderBlockingElements) {
element.remove();
}
}
const timeToUnblockRendering = t - performance.now();
if (timeToUnblockRendering > 0) {
setTimeout(unblockRendering, timeToUnblockRendering);
} else {
unblockRendering();
}
מגבלות האחריות
בעיה נוספת שנתקלנו בה היא שהמעברים בתצוגה בין מסמכים באמצעות כלל ה-@ navigation: auto מתרחשים ברמה הגלובלית בתוך המסמך. אין דרך מובנית להגביל את ההפעלה של מעברי תצוגה בין מסמכים רק ליעדי קליק ספציפיים. מדובר בשינוי גדול מאוד, ולכן לא יכולנו להפעיל את המעברים בין תצוגות של מסמכים ב-100% מהניווטים בחיפוש Google. היינו צריכים דרך להפעיל או להשבית באופן דינמי מעברים בין תצוגות של מסמכים שונים, בהתאם לתכונה שהמשתמש מקיים איתה אינטראקציה. במקרה שלנו, הפעלנו אותם רק לשינויים במצב, למצב AI וממנו. העדכון בוצע באופן אוטומטי באמצעות כלל הניווט, בהתאם ליעד שהמשתמש לחץ עליו או הקיש עליו.
כך מחליפים את כלל ה-at של מעבר התצוגה:
let viewTransitionAtRule: HTMLElement | undefined;
const DISABLED_VIEW_TRANSITION = '@view-transition{navigation:none;}';
const ENABLED_VIEW_TRANSITION = '@view-transition{navigation:auto;}';
function getVtAtRule(): HTMLElement {
if (!viewTransitionAtRule) {
viewTransitionAtRule = document.createElement('style');
document.head.append(viewTransitionAtRule);
}
return viewTransitionAtRule;
}
function disableVt() {
getVtAtRule().textContent = DISABLED_VIEW_TRANSITION;
}
function enableVt() {
getVtAtRule().textContent = ENABLED_VIEW_TRANSITION;
}
Jank ואנימציות מורכבות
חלק מהאנימציות שנוצרו אוטומטית ברכיבי פסאודו של מעבר התצוגה גרמו לירידה בקצב הפריימים במכשירים ישנים יותר, ופגעו בחוויה החלקה והרציפה שרצינו להציע למשתמשים. כדי לשפר את הביצועים של האנימציות, כתבנו אותן מחדש באמצעות טכניקות אנימציה שיכולות לפעול במרכיב ה-compositor. הצלחנו לעשות זאת על ידי בדיקת מסגרות המפתח כדי לקבל את המידות של פסאודו-האלמנטים של התמונה לפני ואחרי, ושימוש במתמטיקה של מטריצות כדי לשכתב את מסגרות המפתח בהתאם. בדוגמה הבאה אפשר לראות איך מקבלים את האנימציה של כל פסאודו-אלמנט של מעבר בין תצוגות:
const pseudoElement = `::view-transition-group(${name})`;
const animation = document
.getAnimations()
.find(
(animation) =>
(animation.effect as KeyframeEffect)?.pseudoElement === pseudoElement,
);
מידע נוסף על כתיבת פריים מרכזי של מעבר תצוגה שפועל בצורה יעילה זמין במאמר View Transitions Applied: Dealing with the Snapshot Containing Block.
דברים נוספים שכדאי לשים לב אליהם
אחת הבעיות הבולטות היא שתיוג רכיבים באמצעות מאפיין ה-CSS view-transition-name משפיע על הקשר של ההערמה (View transitions specification: Section 2.1.1). הייתה בעיה במקור שגרמה לכמה באגים, ולכן היה צורך לשנות את z-index של רכיבי הקונטיינר.
חשוב לזכור גם שאולי לא תרצו להוסיף ערכי view-transition-name לרכיבים כברירת מחדל. הרבה אנשים עובדים על חיפוש Google. כדי למנוע מצב שבו הערכים view-transition-name שהצוות שלנו מגדיר ברכיבים יתנגשו עם ערכים שאנשים מצוותים אחרים עשויים להשתמש בהם, השתמשנו בסוגים של מעברי תצוגה כדי להוסיף את המאפיין view-transition-name באופן מותנה רק בזמן שסוג מסוים של מעבר תצוגה פעיל.
דוגמה ל-CSS להוספת view-transition-name של the-element לרכיב רק כשסוג מעבר התצוגה של ai-mode פעיל:
html:active-view-transition-type(ai-mode) {
#target {
view-transition-name: the-element;
}
}
אחרי שתגדירו את כללי ה-CSS האלה לכל מעברי התצוגה, תוכלו לשנות באופן דינמי את סוג מעבר התצוגה הנוכחי לכל ניווט במהלך האירועים pageswap ו-pagereveal.
דוגמה לעדכון סוג המעבר בין התצוגות ל-ai-mode במהלך האירוע pageswap.
function updateViewTransitionTypes(
event: ViewTransitionEvent,
types: string[],
): void {
event.viewTransition.types.clear();
for (const type of types) {
event.viewTransition.types.add(type);
}
}
window.addEventListener(
'pageswap',
(e) => {
updateViewTransitionTypes(
e as ViewTransitionEvent,
['ai-mode'],
);
}
);
כך אנחנו מונעים התנגשויות בשמות ולא מצלמים שלא לצורך רכיבים שלא צריך לצלם כחלק מהמעבר למצב AI וממנו.
לבסוף, בעיות בהקשר של שכבות יופיעו רק במהלך מעבר התצוגה. כדי לפתור את הבעיות האלה, אפשר לטרגט את ערכי z-index של רכיבי הפסאודו שנוצרו, במקום לשנות באופן שרירותי את ערכי z-index של הרכיבים המקוריים רק כדי לפתור את הבעיה הזו כשמשתמשים במעברי תצוגה.
::view-transition-group(the-element) {
z-index: 100;
}
המאמרים הבאים
אנחנו מתכננים להשתמש במעברים בין תצוגות של מסמכים שונים בחיפוש Google, כולל שילוב עם Navigation API ברגע שהוא יהיה זמין בדפדפנים שונים. כדאי להתעדכן כדי לראות מה נפתח בהמשך!