Live Cursors в мобильном приложении: реализация и оптимизация

Реализация Live Cursors в мобильном приложении Разработчики часто недооценивают сложность live cursors: кажется, что достаточно отправлять координаты по WebSocket и отрисовывать их. Но на практике возникает множество нюансов: задержка сети, разные разрешения экранов, масштабирование, большое коли

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Live Cursors в мобильном приложении: реализация и оптимизация
Сложный
~2-3 дня

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    597

Реализация Live Cursors в мобильном приложении

Разработчики часто недооценивают сложность live cursors: кажется, что достаточно отправлять координаты по WebSocket и отрисовывать их. Но на практике возникает множество нюансов: задержка сети, разные разрешения экранов, масштабирование, большое количество пользователей. Ошибка в любом из этих пунктов делает курсоры неюзабельными. Мы внедряем live cursors с нуля или улучшаем существующую реализацию. В этой статье разберём ключевые технические решения на основе нашего опыта: выбор протокола (Y.js Awareness или кастомный WebSocket), нормализацию координат, интерполяцию анимации и масштабирование при 50+ участниках. Вы узнаете, как избежать типичных ошибок и добиться плавности на мобильных устройствах.

Выбор протокола: Y.js Awareness или кастомный WebSocket

Y.js Awareness Protocol — оптимальное решение, если в приложении уже используется Y.js для синхронизации контента. Awareness хранит ephemeral-состояния: они не персистят, не входят в историю изменений, автоматически удаляются при отключении пользователя.

// Обновляем позицию своего курсора provider.awareness.setLocalStateField('cursor', { x: normalizedX, // в координатах документа, не экрана y: normalizedY, timestamp: Date.now() }); // Подписываемся на изменения чужих курсоров provider.awareness.on('change', ({ updated }) => { updated.forEach(clientId => { if (clientId === provider.awareness.clientID) return; const state = provider.awareness.getStates().get(clientId); if (state?.cursor) { updateRemoteCursor(clientId, state.cursor); } }); }); 

Если Y.js не используется — кастомный WebSocket-канал с throttle 30ms (≈33fps). Более частые обновления не дают заметного улучшения UX, но увеличивают трафик. Серверная сторона может выполнять throttling до 50ms на клиента для экономии ресурсов.

Характеристика Y.js Awareness Кастомный WebSocket
Сложность Низкая (встроен) Средняя (свой протокол)
Персистентность Нет Опционально
Масштабирование До 100+ Ограничено реализацией
Совместимость Только с Y.js Любой бэкенд

Как нормализация координат решает проблему дёрганья?

Критический момент: координаты курсора нужно передавать в системе координат документа, не экрана. У разных пользователей разные zoom-уровни, размеры экрана, положение scroll. Если передавать screen coordinates — курсоры будут прыгать по экрану вместо плавного следования за реальной позицией. Например, при zoom 200% у одного пользователя и 50% у другого, одни и те же экранные координаты соответствуют разным точкам документа.

Формула преобразования: cursorX = (screenX + scrollX) / scale. На приёме обратно: displayX = documentX * scale - scrollX. При смене zoom у получателя позиция курсора автоматически пересчитывается, что исключает рассинхрон.

Как обеспечить плавность при задержке сети?

Raw-позиции от сервера — ступенчатые, особенно при задержке 100–200ms. Нужна интерполяция. В React Native с react-native-reanimated:

const remoteCursorX = useSharedValue(0); const remoteCursorY = useSharedValue(0); // При получении новой позиции от сервера const updateCursor = (x, y) => { remoteCursorX.value = withSpring(x, { damping: 20, stiffness: 300 }); remoteCursorY.value = withSpring(y, { damping: 20, stiffness: 300 }); }; const animStyle = useAnimatedStyle(() => ({ transform: [ { translateX: remoteCursorX.value }, { translateY: remoteCursorY.value }, ] })); 

withSpring добавляет пружинную интерполяцию — курсор «догоняет» реальную позицию плавно. Альтернатива: withTiming с duration: 80 — проще, менее «живой».

На Flutter: AnimationController + Tween<Offset> с CurvedAnimation(curve: Curves.easeOut).

На нативном iOS: UIViewPropertyAnimator с .interruptible option — позволяет прерывать и перезапускать анимацию при новых позициях без артефактов.

Параметр withSpring (RN) withTiming (RN) AnimationController (Flutter)
Ощущение Пружинная, живая Плавная, линейная Кастомная через кривые
Настройка damping, stiffness duration, easing Curve, duration
Производительность Высокая (napi) Высокая Средняя (зависит от кривой)

Почему Canvas-рендеринг лучше 50 отдельных View?

При 50+ пользователях рендеринг каждого курсора как отдельного View создаёт 50+ вьюшек, каждая со своим потоком анимации. Это грузит GPU и увеличивает потребление памяти. Canvas-based рендеринг рисует все курсоры за один проход в одном контексте. На Flutter используем CustomPainter, на React Native — react-native-skia или Canvas из expo-gl. Тесты показывают снижение нагрузки на CPU до 40% при 100 курсорах по сравнению с отдельными View.

Масштабирование: 50+ пользователей

При большом количестве пользователей несколько проблем:

Трафик. N пользователей × 33fps × ~50 байт = при 50 пользователях ~82 KB/s только на cursor updates. Решение: server-side throttling (сервер не пересылает updates чаще чем раз в 50ms на клиента) + отключение курсоров для пользователей за пределами viewport.

Рендеринг. 50 анимированных вьюшек одновременно на мобиле — нагрузка. Используем Canvas-based рендеринг вместо отдельных View для каждого курсора. Рисуем все курсоры в одном CustomPainter / SKCanvas / Canvas за один проход.

Идентификация. При 50 пользователях имя под курсором нечитаемо. Показываем имя только при hover/tap на курсор, остальное время — только цветовая точка с аватаром.

Отображение имени и аватара

Имя пользователя рядом с курсором — классический UX. Реализация: floating label, которая следует за курсором с небольшим offset. Проблема: при движении к краю экрана label выходит за bounds. Нужен clamp — если курсор ближе чем X px к правому краю, label отображается слева от курсора.

Аватар вместо стандартного указателя — часто лучше, чем цветная стрелка. Круглое изображение 24px диаметром, кэшированное в памяти.

Типичные ошибки при интеграции Live Cursors
  • Отсутствие throttle: отправка позиций на каждый пиксель — избыточно. 30 мс — оптимум.
  • Игнорирование координат документа: курсоры "разбегаются" при разных zoom.
  • Отрисовка курсоров как отдельные View при большом количестве: тормозит UI.
  • Необработка отсоединения пользователя: "мёртвые" курсоры остаются на экране.

Пошаговый план интеграции

  1. Настройка WebSocket-канала (Y.js Awareness или кастомный).
  2. Реализация нормализации координат в системе документа.
  3. Интерполяция анимации с настройками под конкретную платформу.
  4. Оптимизация рендеринга (Canvas-подход при >20 курсорах).
  5. Тестирование под нагрузкой (до 200 одновременных курсоров).

Что входит в нашу работу

  • Аудит текущей архитектуры и выбор протокола (Y.js Awareness / кастомный WebSocket).
  • Реализация на вашем стеке: iOS (Swift + Combine), Android (Kotlin + Compose), Flutter, React Native.
  • Интерполяция анимации с настройками под конкретные платформы.
  • Масштабирование до 100+ пользователей с Canvas-рендерингом.
  • Тестирование под нагрузкой (до 200 одновременных курсоров).
  • Документация и передача кода, обучение команды.

Наш опыт: 5 лет разработки коллаборативных мобильных приложений, более 20 проектов с real-time синхронизацией. Гарантируем корректную работу на всех целевых устройствах и версиях ОС.

Сроки и стоимость

Ориентировочные сроки: от 1 недели на прототип до 3 недель на полноценную интеграцию с нагрузочным тестированием. Стоимость рассчитывается индивидуально — зависит от сложности стека и необходимой масштабируемости. Свяжитесь с нами для аудита вашего проекта. Закажите внедрение Live Cursors под ключ — получите консультацию инженера в течение дня.