Пользователи чата, прокручивающие стримы без прерывания чтения
Прокручивающий элемент чата в React, который отслеживает поступающие сообщения в нижней части, замирает, когда пользователь прокручивает вверх, и предоставляет удобные способы вернуться вниз.
Источники: Building a chat interface that doesn’t fight the reader
Принципы прокрутки в компоненте MessageScroller от beUI.
Чтобы перечитать предложение, вы прокручиваете экран вверх. Ассистент всё ещё генерирует текст; как только поступает новый фрагмент данных, область отображения снова перемещается к последней строке.
Вы снова прокручиваете экран вверх.
Красиво оформленные сообщения не могут спасти интерфейс чата, если он создаёт трудности при чтении. Именно такие проблемы были учтены при разработке beUI (beui.dev) — библиотеки компонентов на React, сосредоточенной на анимациях и взаимодействии с пользователем.
Основной вопрос при проектировании MessageScroller заключается в следующем: когда область отображения должна следовать за потоком ответа, а когда оставаться на месте?
Ниже приведено описание состояний.
Примитивный подход заключается в прокрутке до конца при каждой изменении сообщения. Это удобно при просмотре самого свежего ответа, но становится неэффективным, когда нужно изучить более ранний контекст.
Следует рассматривать такие ситуации как отдельные состояния:
- Продолжение просмотра: читатель находится близко к нижней границе, поэтому добавление нового контента может переместить область отображения вверх.
- Чтение более ранних сообщений: читатель уже прошел нижнюю границу, поэтому новый контент не должен тянуть его обратно вниз.
Расстояние от нижней границы прокручиваемой области определяет решение, принимаемое в библиотеке beUI:
const distanceFromEnd =
viewport.scrollHeight -
viewport.scrollTop -
viewport.clientHeight;
const following = distanceFromEnd <= followThreshold;
По умолчанию порог составляет 56 пикселей — это диапазон свободного пространства у нижней границы, позволяющий избежать мгновенного прерывания продолжения просмотра при незначительных движениях.
Целенаправленно избегается полное совпадение значений. Офсеты прокрутки могут быть дробными, даже если показатели высоты кажутся округленными — в документации MDN описывается такое поведение свойства scrollHeight браузера.
Наблюдайте за ростом контента
При потоковой передаче данных часто изменяется существующее сообщение вместо того, чтобы добавлять новую строку. Количество сообщений может оставаться неизменным, в то время как один абзац может стать на несколько строк длиннее.
beUI отслеживает контейнер содержимого с помощью ResizeObserver. При изменении размера он снова проверяет, активен ли следующий элемент перед прокруткой до конца.
Это связывает поведение с отрендеренной геометрией, вместо того чтобы предполагать, что каждый элемент изменяет макет. Загрузка изображений или расширение инструментов, изменяющее высоту, фиксируются таким же образом — ResizeObserver предназначен именно для отслеживания изменений размеров элементов.
Ограничение автоматической прокрутки не замораживает документ. Ранее загруженное изображение без выделенного пространства всё равно может сместить более поздний текст. Стабильный размер медиафайлов и предсказуемый макет сообщений остаются частью функционала продукта.
Читателю необходимо прервать процесс
Программная прокрутка также генерирует события прокрутки. Их нельзя ошибочно интерпретировать как выбор пользователя новой позиции.
MessageScroller отслеживает, скроллится ли он сам. Ввод с колеса мыши, жесты при касании и клавиши вроде Page Up снимают это ограничение, позволяя последующим событиям обновлять соответствующий флаг на основе реального движения читателя.
Это различие имеет значение, когда данные поступают непрерывно. Новые фрагменты должны обновлять текст ответа; они не являются основанием для отбирания фокуса у абзаца, который человек сейчас читает.
Сделайте возврат целенаправленным
После того как читатель дойдет до конца, возврат должен происходить умышленно.
MessageScroller может отображать компактную навигационную полосу над строками сообщений. Каждый маркер содержит предварительный просмотр текста, что позволяет сканировать длинные сообщения без необходимости прокручивания всего текста.
Выбор более раннего маркера отключает функцию прослеживания и перемещает курсор к этому сообщению. Выбор последнего маркера возвращает к концу и снова включает функцию прослеживания. Ручное скроллинг вниз до нижней границы также восстанавливает функцию прослеживания.
Компонент генерирует сигнал onFollowChange, чтобы приложения могли отображать собственный элемент управления «Вернуться к последнему». Этот элемент является частью интерфейса приложения; сам навигатор — это встроенный механизм.
Функция плавного скроллинга должна оставаться опциональной
Плавное прослеживание включено по умолчанию. Пользователи могут отключить его с помощью свойства smooth, и в случае запроса на снижение активности компонент перейдет на мгновенный скроллинг.
Любой из режимов должен оставаться простым с точки зрения логики: прослеживание внизу, возможность чтения выше него, возобновление функции при выборе читателем продолжения.
Движение области просмотра не может заменить сигнализацию доступности. Предоставляйте транскрипт в виде региона в режиме реального времени в стиле журнала с вежливыми обновлениями и флагом загруженности; проверяйте совместимость с вашим рендерером и скрин-ридером, не полагаясь на стандартные настройки.
Проверьте ситуацию, когда кто-то прокручивает страницу
Короткий завершённый диалог не покажет особенностей этой логики. Лучше использовать длинный ответ, который всё ещё передаётся в реальном времени:
- Сохраняйте фокус на последнем результате и убедитесь, что свежие строки остаются видимыми.
- Прокручивайтесь вверх, пока по-прежнему поступают данные, и проверяйте, что область просмотра не прокручивается вниз.
- Используя клавиатуру и панель сообщений, переходите к более старым сообщениям, не теряя местоположения.
- Возвращайтесь к самому новому сообщению и проверяйте, что последующие сообщения снова появляются.
- Повторите те же проверки с включённым уменьшенным движением и с контентом, высота которого меняется (изображения, панели инструментов).
Эти механизмы являются частью того, что делает интерфейс агента удобным в использовании: генерация текста может продолжаться, пока внимание пользователя остается там, где он его разместил.
Демонстрация MessageScroller и исходный код находятся в разделе agents на сайте beUI. Отправьте демонстрационное сообщение, затем прокрутите экран вверх, пока будет поступать ответ.