Пример использования: Как Google создала интегрированный опыт для своего нового режима искусственного интеллекта с использованием переходов между представлениями

Опубликовано: 28 августа 2025 г.

Google Search обладает одним из самых обширных охватов в мире, поэтому изменения в нашем пользовательском опыте могут повлиять на миллиарды пользователей. Мы давно мечтали о веб-интерфейсе, который был бы более современным и похожим на приложение. Когда началась разработка AI Mode , мы хотели создать для наших пользователей интерфейс, в котором переход в AI Mode из стандартного поиска был бы плавным и понятным. Узнав о переходах между представлениями между документами, мы поняли, что это идеальное сочетание для этой функции. В этом примере мы рассказываем о том, что мы узнали, добавляя функцию перехода одновременно с запуском AI Mode.

Запись поиска в Google Поиске, переход из результатов поиска в режим ИИ. Переход осуществляется с помощью переходов между представлениями.

Переходы между представлениями документов — это революционное решение для собственных инструментов браузера, и мы с нетерпением ждем, как оно повлияет на будущее Интернета.

Browser Support

  • Хром: 126.
  • Край: 126.
  • Firefox: не поддерживается.
  • Сафари: 18.2.

Source

Изменение статус-кво

Google Search предъявляет строгие и консервативные требования к поддержке браузерами. Как правило, использование функции с ограниченной доступностью было запрещено. Для переходов между представлениями между документами мы обнаружили, что полифил неэффективен, поскольку основным препятствием было отсутствие API для создания снимков пикселей, а клонирование всей области просмотра приводило к серьёзным проблемам с производительностью. Поэтому использование этой функции в качестве прогрессивного улучшения было наилучшим способом запуска в сочетании с режимом ИИ. Поскольку анимация, создаваемая переходами между представлениями, не влияет напрямую на функциональность веб-сайта, для неподдерживаемого трафика она просто отключалась, что и так соответствовало текущему состоянию производства без анимации переходов.

Мы сначала протестировали эту стратегию прогрессивного улучшения на наших внутренних пользователях. Это обеспечило нам ранние отзывы, которые были исключительно положительными. Полученные отзывы также выявили ошибки, включая проблемы с производительностью и непреднамеренное взаимодействие с другими функциями, например, перекрывающиеся контексты наложения.

Мы сочли эту стратегию успешной, и я полагаю, что в будущем мы попробуем ее и с другими новыми функциями браузера.

Трудности, с которыми мы столкнулись, и их решение

Задержка, блокировка рендеринга и сторожевые таймеры

В целом, дополнительная задержка, возникающая при переходах между представлениями 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. Для обработки таких случаев мы написали скрипт сторожевого таймера, который удаляет элемент 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 Поиске. Нам требовался способ динамически включать или отключать переходы между представлениями документов в зависимости от того, с какой функцией взаимодействует пользователь. В нашем случае мы включили их только для переключения режимов в режим ИИ и обратно. Мы добились этого, программно обновив правило навигации в зависимости от того, какая цель была нажата или коснута.

Способ переключения вида перехода в правиле следующий:

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 и комбинированные анимации

Некоторые автоматически сгенерированные анимации псевдоэлементов перехода между представлениями вызывали пропуски кадров на старых устройствах, что нарушало чёткое и плавное взаимодействие, которое мы стремимся предложить пользователям. Чтобы улучшить производительность анимаций, мы переписали их, используя методы анимации, доступные в компоновщике. Мы смогли это сделать, проверив ключевые кадры, чтобы получить размеры псевдоэлементов снимков «до» и «после», и используя матричную математику для соответствующего переписывания ключевых кадров. В следующем примере показано, как получить анимацию для каждого псевдоэлемента перехода между представлениями:

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

Дополнительную информацию о написании эффективных ключевых кадров перехода представлений см. в статье Примененные переходы представлений: работа с блоком, содержащим снимок .

На что еще следует обратить внимание

Одна из наиболее значимых проблем заключается в том, что маркировка элементов CSS-свойством view-transition-name влияет на контекст наложения ( Спецификация View transitions: Раздел 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'],
    );
  }
);

Таким образом мы предотвращаем конфликты имен и не делаем ненужных снимков элементов, которые не требуют создания снимков при переходе в режим ИИ и выходе из него.

Наконец, любые проблемы с контекстом наложения будут возникать только при переходе между представлениями. Чтобы решить эти проблемы, мы можем ориентироваться на z-индексы сгенерированных псевдоэлементов, вместо того чтобы произвольно изменять z-индексы исходных элементов только для решения этой проблемы при переходе между представлениями.

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

Что дальше?

Мы планируем использовать кросс-браузерные переходы между представлениями в Google Поиске, включая интеграцию с Navigation API, как только он станет доступен для всех браузеров. Следите за новостями, чтобы узнать, что мы создадим дальше!