פורסם: 28 באוגוסט 2025
לחיפוש Google יש את אחד מטווח ההגעה הגדולים בעולם, ולכן שינויים בחוויית המשתמש שלנו יכולים להשפיע על מיליארדי משתמשים. חלמנו כבר הרבה זמן על חוויית שימוש באתר שתהיה מודרנית יותר ודומה יותר לשימוש באפליקציה. כשנכנסנו לפיתוח של מצב AI, רצינו ליצור חוויה למשתמשים שבה המעבר מחיפוש רגיל למצב AI יהיה חלק ורציף. כששמענו על מעברים בין תצוגות של מסמכים שונים, ידענו שזה יהיה שילוב מושלם לתכונה. במקרה לדוגמה הזה אנחנו משתפים את מה שלמדנו כששילבנו את תכונת המעבר עם ההשקה של מצב AI.
מעברים בין תצוגות במסמכים שונים הם פריצת דרך בכל מה שקשור לכלים מקוריים של דפדפנים, ואנחנו נרגשים לראות איך הם ישפיעו על האינטרנט בעתיד.
שינוי הסטטוס קוו
לחיפוש Google יש דרישות מחמירות ושמרניות לתמיכה בדפדפנים. באופן כללי, השימוש בתכונה עם זמינות מוגבלת היה אסור. במעברים בין תצוגות במסמכים שונים, גילינו שאי אפשר להשתמש ב-polyfill, והבעיה העיקרית הייתה שאין API לצילום תמונת מצב של פיקסלים, ושיבוט של כל אזור התצוגה גרם לבעיות ביצועים משמעותיות. לכן, השימוש בתכונה כשיפור הדרגתי היה הדרך הטובה ביותר להשיק אותה לצד מצב AI. מכיוון שהאנימציות שנוצרות על ידי מעברים בין תצוגות לא משפיעות ישירות על הפונקציונליות של האתר, במקרה של תנועה לא נתמכת הן פשוט יושבתו, וזה כבר המצב הנוכחי בייצור ללא אנימציות מעבר.
בדקנו את אסטרטגיית השיפור המתקדם הזו קודם כל עם משתמשים פנימיים. כך קיבלנו משוב מוקדם, שהיה חיובי ברובו. בנוסף, קיבלנו משוב על באגים, כולל בעיות בביצועים ואינטראקציות לא מכוונות עם תכונות אחרות, כמו הקשרים חופפים של שכבות.
האסטרטגיה הזו הוכיחה את עצמה, ואני מאמין שננסה אותה גם עם תכונות חדשות אחרות בדפדפן בעתיד.
קשיים שנתקלנו בהם ופתרונות
חביון, חסימת עיבוד וטיימרים של watchdog
בסך הכול, זמן האחזור הנוסף שנובע ממעברי תצוגה של 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 וממנו. עשינו זאת באמצעות עדכון תוכניתי של כלל הניווט בהתאם ליעד שלחצו עליו או הקישו עליו.
כדי להפעיל או להשבית את כלל המעבר בין תצוגות:
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 ברגע שהוא יהיה זמין בדפדפנים שונים. בהמשך נפרסם עדכונים על הפיתוחים הבאים שלנו.