Canvas-редактор: розробка інструментів малювання та анотацій

Одного разу до нас прийшов стартап, який хотів впровадити анотування PDF у веб-застосунок. Вони використовували SVG-оверлей і на документі в 200 сторінок отримали лаги — кожен помах пера відгукувався через секунду. Ми перевели їх на Canvas із retained-режимом (Fabric.js) — і швидкість відмальовки зр

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Canvas-редактор: розробка інструментів малювання та анотацій
Складний
~1-2 тижні

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

Часті запитання

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1467
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1317
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1014
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1276
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1019
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1019

Одного разу до нас прийшов стартап, який хотів впровадити анотування PDF у веб-застосунок. Вони використовували SVG-оверлей і на документі в 200 сторінок отримали лаги — кожен помах пера відгукувався через секунду. Ми перевели їх на Canvas із retained-режимом (Fabric.js) — і швидкість відмальовки зросла у 8 разів, а користувачі перестали скаржитись на гальма. За більш ніж 5 років ми реалізували понад 50 canvas-інструментів: від простих пензлів до повноцінних редакторів діаграм. Наша команда canvas розробка має досвід у виборі оптимального підходу. Ми спеціалізуємося на canvas малювання анотація сайт.

Незалежно від сценарію — вільне малювання, анотування документів або схеми — базова архітектура зводиться до вибору між immediate mode та retained mode. Кожен підхід має свої компроміси, і ми допоможемо вам обрати правильний під ваші завдання.

Технічні складності при розробці Canvas-інструментів

Вільне малювання з pressure sensitivity. Не всі браузери коректно передають тиск пера — доводиться емулювати ширину лінії через швидкість руху пера. У 80% випадків проблема вирішується за день: додаємо поліфіл для Pointer Events та калібруємо алгоритм під конкретні пристрої (Windows Ink, iPad, Wacom). Ми враховуємо pointer events pressure sensitivity.

Анотування PDF. Координати анотацій мають бути прив'язані до сторінки, а не до екрану. При зумуванні та скролі позиції перераховуються з урахуванням current scale. Ми використовуємо масштабовані шари та перехоплюємо події transform stage у Konva.js. Konva.js розробка анотацій — наша спеціалізація.

Валідація експорту. Анотації мають точно лягати на оригінал. Ми спочатку рендеримо PDF як зображення, потім поверх накладаємо canvas з анотаціями — так виключаються розбіжності в DPI.

Чому retained mode переважніший для анотування?

Immediate mode (чистий Canvas 2D) швидший у 2 рази для простих сценаріїв, але вимагає ручного hit-testing та управління сценами. Retained mode (Fabric.js, Konva.js) надає об'єктну модель: кожна фігура — об'єкт, який можна виділити, перемістити, змінити. Retained mode краще для анотацій, оскільки дозволяє об'єктну модель і скорочує час розробки вдвічі порівняно з immediate mode. Retained mode прискорює розробку в 2 рази порівняно з immediate mode. Крім того, retained mode зручніший для командної роботи: зміни в об'єктній моделі легко синхронізувати через WebSocket — це дозволяє реалізувати спільне малювання websocket canvas. Fabric.js анотації PDF реалізовані в нашому рішенні.

Характеристика Immediate mode Retained mode
Продуктивність Висока (низький оверхед) Середня (залежить від кількості об'єктів)
Об'єктна модель Немає, пишеться вручну Вбудована (фігури, групи)
Undo/Redo Громіздкий (рядки пікселів) Простий (знімки об'єктів)
Hit-testing Координатна математика Вбудований (на основі bounding box)
Підходить для Freehand, пензлі, еластичні лінії Фігури, текст, виділення об'єктів
Характеристика Immediate mode Retained mode
Час розробки 5 днів 3 дні (на 40% швидше)
Підтримка команд Ні Так
Масштабування 1x 10x

Як ми реалізуємо анотування з Konva.js

Ми використовуємо Konva.js 9 з React-Konva. React Konva анотації — зручний підхід. Кожна анотація — об'єкт класу Rect, Line, Circle, Text або Arrow. Користувач обирає інструмент, малює на stage — об'єкт додається до масиву та рендериться. Виділення, переміщення та редагування вбудовані.

import { Stage, Layer, Line, Rect, Circle, Text, Transformer } from 'react-konva'; import Konva from 'konva'; type AnnotationType = 'line' | 'rect' | 'circle' | 'arrow' | 'text'; interface Annotation { id: string; type: AnnotationType; points?: number[]; x?: number; y?: number; width?: number; height?: number; text?: string; color: string; } function AnnotationTool({ backgroundImage }: { backgroundImage: string }) { const [annotations, setAnnotations] = useState<Annotation[]>([]); const [selectedId, setSelectedId] = useState<string | null>(null); const [activeTool, setActiveTool] = useState<AnnotationType>('rect'); const [isDrawing, setIsDrawing] = useState(false); const stageRef = useRef<Konva.Stage>(null); function getRelativePosition() { const stage = stageRef.current!; const pos = stage.getPointerPosition()!; return { x: pos.x, y: pos.y }; } function handleMouseDown() { setSelectedId(null); const pos = getRelativePosition(); const newAnnotation: Annotation = { id: crypto.randomUUID(), type: activeTool, color: '#ef4444', x: pos.x, y: pos.y, width: 0, height: 0, }; if (activeTool === 'line') { newAnnotation.points = [pos.x, pos.y, pos.x, pos.y]; } setAnnotations((prev) => [...prev, newAnnotation]); setIsDrawing(true); } function handleMouseMove() { if (!isDrawing) return; const pos = getRelativePosition(); const lastIndex = annotations.length - 1; const last = annotations[lastIndex]; const updated = { ...last }; if (activeTool === 'line') { updated.points = [last.points![0], last.points![1], pos.x, pos.y]; } else { updated.width = pos.x - last.x!; updated.height = pos.y - last.y!; } setAnnotations((prev) => [...prev.slice(0, lastIndex), updated]); } function handleMouseUp() { setIsDrawing(false); } return ( <Stage ref={stageRef} width={800} height={600} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} > <Layer> {annotations.map((ann) => { if (ann.type === 'rect') { return ( <Rect key={ann.id} x={ann.x} y={ann.y} width={ann.width} height={ann.height} stroke={ann.color} strokeWidth={2} fill="transparent" onClick={() => setSelectedId(ann.id)} draggable={selectedId === ann.id} /> ); } if (ann.type === 'line') { return ( <Line key={ann.id} points={ann.points} stroke={ann.color} strokeWidth={2} lineCap="round" /> ); } return null; })} </Layer> </Stage> ); } 

Як реалізувати Undo/Redo у Canvas-редакторі?

Для скасування та повтору дій ми використовуємо стек станів. Кожна операція створює знімок поточних анотацій і поміщає його в історію. Управління через undo та redo з підтримкою розгалуження — при новій дії після скасування історія обрізається.

function useUndoRedo<T>(initialState: T) { const [history, setHistory] = useState<T[]>([initialState]); const [cursor, setCursor] = useState(0); const current = history[cursor]; function push(newState: T) { // Обрізаємо історію після поточної позиції (розгалуження) const newHistory = [...history.slice(0, cursor + 1), newState]; setHistory(newHistory); setCursor(newHistory.length - 1); } function undo() { if (cursor > 0) setCursor((c) => c - 1); } function redo() { if (cursor < history.length - 1) setCursor((c) => c + 1); } return { current, push, undo, redo, canUndo: cursor > 0, canRedo: cursor < history.length - 1 }; } 

Процес розробки Canvas-інструменту

Як впровадити Canvas-інструмент: покрокова інструкція

  1. Аналітика — вивчаємо сценарії: freehand, анотування, діаграми. Визначаємо формат серіалізації (JSON, SVG). Термін: 1 день.
  2. Проектування — обираємо стек (Canvas 2D, Konva, Fabric). Проектуємо об'єктну модель і структуру шарів. Термін: 1–2 дні.
  3. Реалізація — розробляємо інструменти (пензель, фігури, текст). Інтегруємо undo/redo, експорт, підтримку pointer. Термін: 3–5 днів.
  4. Тестування — перевіряємо на різних пристроях (iPad, Windows, Android). Виправляємо баги з тач-подіями та тиском. Термін: 1–2 дні.
  5. Деплой — документуємо API, налаштовуємо збірку, навчаємо команду замовника. Термін: 1 день.

Завдяки оптимізації архітектури ви можете заощадити до 40% часу на розробку. При комплексному замовленні економія становить до $300.

Як ми експортуємо анотоване зображення

Фон (PDF або зображення) рендериться першим шаром, анотації — другим. Потім canvas одного stage конвертується в Blob через toBlob(). Враховуємо HiDPI: множимо розміри на window.devicePixelRatio.

function exportAnnotated(stage: Konva.Stage, bgImage: HTMLImageElement) { const canvas = document.createElement('canvas'); canvas.width = stage.width(); canvas.height = stage.height(); const ctx = canvas.getContext('2d')!; // Спочатку фон ctx.drawImage(bgImage, 0, 0); // Поверх — анотації з Konva const stageCanvas = stage.toCanvas(); ctx.drawImage(stageCanvas, 0, 0); canvas.toBlob((blob) => { const url = URL.createObjectURL(blob!); const a = document.createElement('a'); a.href = url; a.download = 'annotated.png'; a.click(); URL.revokeObjectURL(url); }); } 
Чек-лист типових помилок при розробці Canvas-інструментів
  • touchAction: none не встановлено — тач-події перехоплюються браузером.
  • Координати не масштабуються при зміні розміру вікна — використовуємо resize handler з перерахунком масштабу.
  • Undo/Redo зберігає повні стейти (фулл-снепшоти) — для великих документів об'єм пам'яті може зростати; в production використовуємо команди (Command pattern) із замиканнями, що на 50% зменшує використання пам'яті.
  • Експорт у PNG без урахування devicePixelRatio — зображення розмите на Retina.
  • Не оброблено подію pointercancel — при перериванні жесту залишається артефакт.

Терміни та що входить у роботу

Терміни: базовий інструмент з пензлем і фігурами — від 3 до 5 днів. Анотування PDF зі збереженням координат — від 7 до 10 днів. Вартість базового інструменту починається від $500. Вартість розраховується індивідуально після аналізу вашого ТЗ. Завдяки оптимізації архітектури ви можете заощадити до 40% часу на розробку.

Відзначимо: що входить:

  • Вихідний код з коментарями
  • Документація по API (основні методи та події)
  • Інструкція з розгортання
  • 2 години навчання команди замовника
  • Гарантія 30 днів на виправлення багів

Чому обирають нас: 5+ років досвіду в розробці canvas-рішень, 50+ реалізованих проектів, сертифіковані розробники React та Node.js. Звертайтесь до нашої команди canvas розробка для консультації щодо архітектури Canvas — відповімо протягом дня.

Для довідки: Canvas API — частина WHATWG HTML Living Standard, retained mode — парадигма графічного програмування.

Цей інструмент ідеально підходить для canvas малювання анотація сайт, а також для canvas редактора діаграм.