Реалізація Whiteboard (спільна дошка) в мобільному додатку
Спільна дошка — нескінченний холст з об'єктами: лінії, фігури, стикери, зображення, текст. Кілька користувачів рисують одночасно, бачать дії один одного в реальному часі. На мобілі додаються: пальцевий вхід, pinch-to-zoom, Apple Pencil / Stylus з pressure sensitivity.
Найскладніша частина тут — не мережевий рівень, а рендеринг холста та обробка жестів під навантаженням.
Архітектура даних: об'єктна модель холста
Кожен об'єкт на дошці — документ у distributed state. Добре підходить Y.Map де ключ — UUID об'єкта, значення — його властивості (тип, координати, розмір, колір, z-index). Для складних об'єктів (шлях з точок) — Y.Array точок всередину об'єкта.
const yobjects = ydoc.getMap('objects');
// Додаємо стрілку
yobjects.set(uuid(), {
type: 'arrow',
x1: 100, y1: 200,
x2: 400, y2: 350,
strokeColor: '#1a1a2e',
strokeWidth: 2
});
Конкурентна переміщення одного об'єкта двома користувачами: Last-Write-Wins — прийнятно для координат. Конкурентне видалення об'єкта одним та зміна іншим — стандартна проблема CRDT: операція зміни застосовується до вже видаленого об'єкта та втрачається. Потрібна tombstone-логіка або тимчасове зберігання «привидів» об'єктів.
Рисування кистю: стриминг точок
Вільне рисування генерує 60–120 точок на секунду (на 60Hz дисплеї). Відправляти кожну точку через WebSocket — надмірно. Оптимізація:
- Буферизація — відправляємо batch точок кожні 50ms.
- Алгоритм упрощення — Douglas-Peucker або Ramer-Douglas-Peucker видаляє надлишкові точки з configurable epsilon. Крива з 500 точок стискається до 30–50 без видимої втрати якості.
-
Stroke prediction — на iOS з Apple Pencil
UITouch.predictedTouchesпередбачають наступні точки до їхнього фактичного попадання, знижуючи сприйняту затримку.
Stroke як CRDT-об'єкт: починаємо з тимчасового Y.Array точок в Awareness (не в документі — не потрібна повна історія кожної точки). При завершенні (touchEnd / pointerUp) — фіксуємо упрощений шлях у документ як єдиний об'єкт.
Canvas рендеринг на мобілі
React Native: react-native-skia (Skia graphics engine) — найкращий варіант для продуктивного 2D-рендеринґу. @shopify/react-native-skia підтримує Path, Paint, Text, Image. Рендер на GPU через RN's new architecture. react-native-svg — простіше, але повільніше для анімованих об'єктів.
Flutter: CustomPainter з Canvas API — нативний шлях. flutter_drawing_board — готовий пакет з базовими інструментами. Для production: кастомний CustomPainter з dirty-region optimization (перерисовуємо лише змінений регіон через Canvas.clipRect).
iOS Native: Metal + MetalKit для максимальної продуктивності, UIBezierPath + CALayer для середнього рівня складності. Apple PencilKit — готовий компонент з підтримкою Pencil, але обмежена кастомізація.
Android Native: Canvas API з Path для простих випадків, OpenGL ES / Vulkan через GLSurfaceView для складних.
Віртуалізація нескінченного холста
При 1000+ об'єктах рендеринг всього холста у кожному кадрі — проблема. Потрібен spatial index (R-tree або простий grid-based) для визначення об'єктів у текущому viewport. Рендеримо лише видимі об'єкти + невеликий буфер за краями viewport.
rbush — JavaScript R-tree бібліотека, працює у React Native. При panZoom визначаємо новий viewport, запрашуємо об'єкти в цьому bounding box, рендеримо лише їх.
Синхронізація: що відправляти й коли
Cursor/viewport awareness (позиція користувача на холсті) — через Y.js Awareness, не в документ, 10fps достатньо.
Об'єкти у процесі створення — два режими:
- Тимчасовий preview через Awareness (інші бачать нефіксований об'єкт).
- Тільки фінальний об'єкт після
touchEnd(простіше, але немає real-time preview рисування).
Перший режим дає кращий UX, другий — менше трафіку й складності.
Оцінка
Базовий whiteboard (фігури, текст, стрілки, синхронізація) на Flutter або React Native — 10–16 тижнів. З рисуванням кистю, pressure sensitivity, розумним упрощенням шляхів та віртуалізацією великого холста — 20–32 тижні.







