Studium przypadku: jak Google stworzyło połączony interfejs dla nowego trybu AI za pomocą przejść widoku

Data publikacji: 28 sierpnia 2025 r.

Wyszukiwarka Google ma jeden z największych zasięgów na świecie, dlatego zmiany w interfejsie mogą mieć wpływ na miliardy użytkowników. Od dawna marzyliśmy o stronie internetowej, która byłaby bardziej nowoczesna i przypominała aplikację. Gdy rozpoczęliśmy prace nad trybem AI, chcieliśmy stworzyć dla użytkowników środowisko, w którym przejście z wyszukiwania standardowego do trybu AI będzie płynne i spójne. Gdy dowiedzieliśmy się o przejściach między dokumentami, wiedzieliśmy, że to idealne rozwiązanie dla tej funkcji. W tym studium przypadku opisujemy, czego dowiedzieliśmy się podczas dodawania funkcji przejścia wraz z wprowadzeniem trybu AI.

Nagranie wyszukiwania w wyszukiwarce Google, przełączania się z wyników wyszukiwania na tryb AI. Przejście korzysta z przejść widoku.

Przejścia między dokumentami to rewolucyjne rozwiązanie w zakresie natywnych narzędzi przeglądarki. Z niecierpliwością czekamy na to, jak wpłynie ono na przyszłość internetu.

Browser Support

  • Chrome: 126.
  • Edge: 126.
  • Firefox: not supported.
  • Safari: 18.2.

Source

Zmiana status quo

Wyszukiwarka Google ma rygorystyczne i konserwatywne wymagania dotyczące obsługi przeglądarek. Z reguły korzystanie z funkcji o ograniczonej dostępności było niedozwolone. W przypadku przejść między dokumentami stwierdziliśmy, że polyfill nie jest możliwy do zastosowania. Główną przeszkodą był brak interfejsu API do tworzenia migawek pikseli, a klonowanie całego obszaru wyświetlania powodowało poważne problemy z wydajnością. Dlatego najlepszym sposobem na wprowadzenie tej funkcji wraz z trybem AI było zastosowanie jej jako progresywnego ulepszenia. Animacje utworzone przez przejścia widoku nie mają bezpośredniego wpływu na funkcjonalność witryny, więc w przypadku nieobsługiwanego ruchu po prostu zostaną wyłączone. Tak było już w przypadku wersji produkcyjnej bez animacji przejść.

Najpierw przetestowaliśmy tę strategię stopniowego ulepszania na naszych użytkownikach wewnętrznych. Dzięki temu otrzymaliśmy wczesne opinie, które były w większości pozytywne. Otrzymane opinie ujawniły też błędy, w tym problemy z wydajnością i niezamierzone interakcje z innymi funkcjami, np. nakładające się konteksty układania.

Ta strategia okazała się skuteczna i wierzę, że będziemy ją stosować w przypadku innych nowych funkcji przeglądarki.

Trudności, które napotkaliśmy, i sposoby ich rozwiązania

Opóźnienie, blokowanie renderowania i liczniki czasu watchdoga

Ogólnie rzecz biorąc, dodatkowe opóźnienie związane z przejściami widoku w przypadku aplikacji wielostronicowych jest pomijalne w 99% przypadków, zwłaszcza na nowoczesnym sprzęcie. Wyszukiwarka Google ma jednak bardzo wysokie wymagania dotyczące opóźnień i staramy się tworzyć interfejsy, które działają dobrze na wszystkich urządzeniach. Dla nas liczą się nawet milisekundy, dlatego musieliśmy zainwestować w odpowiednie wdrożenie przejść między widokami w różnych dokumentach bez pogarszania wrażeń użytkowników.

Blokowanie renderowania to technika, która dobrze sprawdza się w przypadku przejść między widokami w różnych dokumentach. Migawki pseudoelementów w dokumencie przychodzącym mogą wyświetlać tylko treści, które zostały już wyrenderowane. Aby animować treści z dokumentu przychodzącego, musisz renderować blok, dopóki nie zostanie wyrenderowany element docelowy, który chcesz animować. Aby to zrobić, użyj atrybutu blocking w tagu HTMLLinkElement. Blokowanie renderowania ma swoje wady, ponieważ oczekiwanie na element znajdujący się pod koniec drzewa DOM dokumentu przychodzącego może mieć znaczny wpływ na opóźnienie. Musieliśmy odpowiednio zrównoważyć tę zależność i wyrenderować blok tylko w przypadku elementów, które są renderowane bardzo wcześnie w cyklu życia strony.

<!-- 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>

W niektórych przypadkach precyzyjne określenie elementu, w którym ma być renderowany blok, nie wystarczało. Niektóre urządzenia lub połączenia nadal będą miały większe opóźnienie, nawet jeśli blokowanie renderowania będzie dotyczyć elementu znajdującego się blisko początku drzewa DOM. Aby sobie z tym poradzić, napisaliśmy skrypt minutnika nadzorującego, który po upływie określonego czasu usuwa znak HTMLLinkElement, aby wymusić odblokowanie renderowania przychodzącego dokumentu.

Możesz to zrobić w prosty sposób:

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();
}

Ograniczenia ochrony

Inny problem, który napotkaliśmy, polega na tym, że reguła @ navigation: auto przejść widoku między dokumentami występuje na poziomie globalnym w dokumencie. Nie ma wbudowanego sposobu na ograniczenie włączania przejść między widokami dokumentów tylko do określonych celów kliknięć. Ze względu na to, że jest to duża zmiana, nie mogliśmy włączyć przejść między dokumentami w przypadku wszystkich nawigacji w wyszukiwarce Google. Potrzebowaliśmy sposobu na dynamiczne włączanie i wyłączanie przejść między widokami w różnych dokumentach w zależności od tego, z jakiej funkcji korzysta użytkownik. W naszym przypadku włączyliśmy je tylko w przypadku zmian trybu na tryb AI i z niego. Zrobiliśmy to, automatycznie aktualizując regułę @navigation w zależności od tego, który element docelowy został kliknięty lub dotknięty.

Aby przełączać regułę @view-transition:

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;
}

Niespójności i animacje złożone

Niektóre automatycznie generowane animacje w pseudo-elementach przejścia widoku powodowały spadek liczby klatek na starszych urządzeniach, co pogarszało płynność i wygodę korzystania ze strony. Aby zwiększyć wydajność animacji, przepisaliśmy je, używając technik animacji, które mogą działać w kompozytorze. Udało nam się to zrobić, sprawdzając klatki kluczowe, aby uzyskać wymiary pseudoelementów przed i po zrzucie ekranu, a następnie używając matematyki macierzowej do odpowiedniego przepisania klatek kluczowych. W przykładzie poniżej pokazujemy, jak pobrać animację dla każdego pseudoelementu przejścia widoku:

const pseudoElement = `::view-transition-group(${name})`;
const animation = document
  .getAnimations()
  .find(
    (animation) =>
      (animation.effect as KeyframeEffect)?.pseudoElement === pseudoElement,
  );

Więcej informacji o tworzeniu wydajnych klatek kluczowych przejścia widoku znajdziesz w artykule View Transitions Applied: Dealing with the Snapshot Containing Block (Przejścia widoku: radzenie sobie z blokiem zawierającym migawkę).

Inne kwestie, na które warto zwrócić uwagę

Jednym z najważniejszych problemów jest to, że oznaczanie elementów za pomocą właściwości CSS view-transition-name wpływa na kontekst układania (specyfikacja przejść widoku: sekcja 2.1.1). Było to źródłem wielu błędów, które wymagały modyfikacji z-index elementów kontenera.

Pamiętaj też, że domyślnie nie warto dodawać do elementów wartości view-transition-name. Nad wyszukiwarką Google pracuje wiele osób. Aby zapobiec konfliktom między wartościami view-transition-name ustawianymi przez nasz zespół w elementach a wartościami, których mogą używać osoby z innych zespołów, zastosowaliśmy typy przejść widoku, aby warunkowo dodawać właściwość view-transition-name tylko wtedy, gdy aktywny jest określony typ przejścia widoku.

Przykładowy kod CSS, który dodaje view-transition-name elementu the-element tylko wtedy, gdy aktywny jest typ przejścia widoku ai-mode:

html:active-view-transition-type(ai-mode) {
  #target {
    view-transition-name: the-element;
  }
}

Gdy masz już te reguły CSS dla wszystkich przejść widoku, możesz dynamicznie zmieniać bieżący typ przejścia widoku w przypadku dowolnej nawigacji podczas zdarzeń pageswappagereveal.

Przykład zmiany typu przejścia widoku na ai-mode podczas zdarzenia 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'],
    );
  }
);

W ten sposób zapobiegamy kolizjom nazw i niepotrzebnie nie robimy zrzutów elementów, które nie muszą być uwzględniane podczas przechodzenia do trybu AI i z niego.

Na koniec dodajmy, że wszelkie problemy z kontekstem układania będą występować tylko podczas przejścia widoku. Aby rozwiązać te problemy, możemy kierować reklamy na indeksy z wygenerowanych pseudoelementów, zamiast dowolnie modyfikować indeksy z oryginalnych elementów tylko po to, aby rozwiązać ten problem podczas korzystania z przejść widoku.

::view-transition-group(the-element) {
  z-index: 100;
}

Co dalej?

Planujemy używać przejść widoku między dokumentami w wyszukiwarce Google, w tym integracji z interfejsem Navigation API, gdy tylko stanie się on dostępny w różnych przeglądarkach. Zaglądaj tu, aby zobaczyć, co jeszcze stworzymy.