Розробка онлайн-графічного редактора (Canva-подібний)

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка онлайн-графічного редактора (Canva-подібний)
Складна
від 2 тижнів до 3 місяців
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка онлайн-графічного редактора (Canva-подібний)

Онлайн-редактор з canvas-робочою областю, переміщуваними/масштабованими об'єктами, шарами та експортом зображень — одна з технічно складних задач у фронтенд-розробці. Правильний вибір рендеринг-рушія визначає 80% архітектури.

Вибір рушія

Рушій Коли використовувати
Fabric.js Готове рішення для canvas-редактора, велика екосистема, але застаріле API
Konva.js React-friendly (react-konva), хороша продуктивність, активна підтримка
Pixi.js Високопродуктивний WebGL рендеринг, для складних ефектів
SVG (custom) Для простих редакторів з невеликою кількістю об'єктів, легко стилізувати CSS
tldraw Open-source, whiteboard-like, React, підходить для діаграм

Для Canva-подібного редактора з текстом, фігурами, зображеннями та експортом — Konva.js оптимальний.

Архітектура стану

Центральна структура — дерево об'єктів (elements), яке рендерится в canvas. Стан керується через Zustand або Redux:

interface EditorElement {
  id:       string;
  type:     'rect' | 'circle' | 'text' | 'image' | 'group';
  x:        number;
  y:        number;
  width:    number;
  height:   number;
  rotation: number;
  opacity:  number;
  zIndex:   number;
  locked:   boolean;
  visible:  boolean;
  fill?:    string;
  stroke?:  string;
  strokeWidth?: number;
  text?:    string;
  fontSize?: number;
  fontFamily?: string;
  src?:     string; // для зображення
}

interface EditorState {
  elements:        EditorElement[];
  selectedIds:     string[];
  canvasWidth:     number;
  canvasHeight:    number;
  zoom:            number;
  history:         EditorElement[][];
  historyIndex:    number;
}

Рендеринг через react-konva

import { Stage, Layer, Rect, Circle, Text, Image, Transformer } from 'react-konva';

const Canvas: React.FC = () => {
  const { elements, selectedIds, zoom } = useEditorStore();
  const trRef = useRef<Konva.Transformer>(null);
  const selectedNodes = useRef<Konva.Node[]>([]);

  useEffect(() => {
    if (trRef.current) {
      trRef.current.nodes(selectedNodes.current);
      trRef.current.getLayer()?.batchDraw();
    }
  }, [selectedIds]);

  return (
    <Stage
      width={canvasWidth * zoom}
      height={canvasHeight * zoom}
      scaleX={zoom}
      scaleY={zoom}
      onMouseDown={handleStageClick}
    >
      <Layer>
        {elements
          .filter(el => el.visible)
          .sort((a, b) => a.zIndex - b.zIndex)
          .map(el => (
            <EditorElement
              key={el.id}
              element={el}
              isSelected={selectedIds.includes(el.id)}
            />
          ))}
        <Transformer ref={trRef} rotateEnabled={true} keepRatio={false} />
      </Layer>
    </Stage>
  );
};

Undo / Redo

Історія — снапшоти масиву elements:

const commit = () => {
  const { elements, history, historyIndex } = store;
  const newHistory = history.slice(0, historyIndex + 1);
  newHistory.push(JSON.parse(JSON.stringify(elements)));
  store.setState({
    history: newHistory.slice(-50),
    historyIndex: newHistory.length - 1,
  });
};

const undo = () => {
  const { history, historyIndex } = store;
  if (historyIndex <= 0) return;
  store.setState({
    elements: JSON.parse(JSON.stringify(history[historyIndex - 1])),
    historyIndex: historyIndex - 1,
  });
};

Гарячі клавіші через useHotkeys:

useHotkeys('ctrl+z', undo);
useHotkeys('ctrl+shift+z, ctrl+y', redo);
useHotkeys('ctrl+d', duplicateSelected);
useHotkeys('delete, backspace', deleteSelected);
useHotkeys('ctrl+a', selectAll);

Завантаження та обробка зображень

Завантаження в об'єктне сховище (S3) з ресайзом через Sharp:

// Backend: POST /api/editor/upload
const processUpload = async (file: File): Promise<string> => {
  const buffer = await file.arrayBuffer();
  const resized = await sharp(Buffer.from(buffer))
    .resize(2000, 2000, { fit: 'inside', withoutEnlargement: true })
    .jpeg({ quality: 85 })
    .toBuffer();

  const key = `editor-uploads/${uuid()}.jpg`;
  await s3.upload({ Bucket: BUCKET, Key: key, Body: resized }).promise();

  return `${CDN_URL}/${key}`;
};

На клієнті — drag-and-drop зона та паста з буфера:

document.addEventListener('paste', (e) => {
  const items = e.clipboardData?.items;
  for (const item of items ?? []) {
    if (item.type.startsWith('image/')) {
      const file = item.getAsFile();
      if (file) addImageElement(file);
    }
  }
});

Текстовий редактор

Вбудоване редагування тексту через подвійний клік:

const EditableText: React.FC<TextElementProps> = ({ element, onUpdate }) => {
  const [isEditing, setIsEditing] = useState(false);

  const handleDblClick = (e: Konva.KonvaEventObject<MouseEvent>) => {
    setIsEditing(true);
    const pos = e.target.getAbsolutePosition();
    showTextArea({ x: pos.x, y: pos.y, element, onSave: (text) => {
      onUpdate({ ...element, text });
      setIsEditing(false);
    }});
  };

  return (
    <Text
      {...element}
      onDblClick={handleDblClick}
      visible={!isEditing}
    />
  );
};

Експорт

const exportCanvas = async (format: 'png' | 'jpeg' | 'svg', quality = 1) => {
  const stage = stageRef.current;

  if (format === 'png' || format === 'jpeg') {
    const dataUrl = stage.toDataURL({
      mimeType: `image/${format}`,
      quality,
      pixelRatio: 2,
    });
    downloadFile(dataUrl, `design.${format}`);
  }

  if (format === 'svg') {
    const svg = serializeToSVG(elements);
    const blob = new Blob([svg], { type: 'image/svg+xml' });
    downloadFile(URL.createObjectURL(blob), 'design.svg');
  }
};

Термін

Етап Час
Базовий canvas (фігури, виділення, трансформація) 3–4 дні
Текст + зображення 2–3 дні
Undo/Redo + гарячі клавіші 1–2 дні
Шари (порядок, видимість, блокування) 1–2 дні
Експорт (PNG, JPEG, SVG) 1–2 дні
Збереження / шаблони / автозбереження 2 дні
Текстові стилі, вирівнювання, міжстрочний інтервал 2 дні

Мінімальний робочий редактор: 10–14 робочих днів.