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

Ваші користувачі малюють, роблять позначки та анотації прямо на сайті, але редактор гальмує, а експорт спотворює результат? Ми розробляємо canvas-інструменти з нуля або покращуємо наявні, використовуючи перевірені підходи та бібліотеки. Наша команда реалізує проєкт під ключ — від вибору архітектури до впровадження та подальшої підтримки, забезпечуючи плавну роботу й точний експорт.

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

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

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

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

Одного разу до нас прийшов стартап, який хотів впровадити анотування 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 редактора діаграм.

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

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

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1141
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060