javascript

Canvas или SVG для карты офиса: как мы выбрали и справились с неочевидными граблями

  • вторник, 30 июня 2026 г. в 00:00:12
https://habr.com/ru/articles/1052156/

Если к вам придут с задачей внедрения интерактивной карты офиса, какой рендер вы выберете? Canvas или SVG? Верхнеуровневый обзор скажет, что Canvas хорош для частых перерисовок (географические карты, игры), а SVG — когда важна работа с отдельными элементами, которые естественным образом присутствуют в DOM.

Мы выбрали SVG. В этой статье не будет глубокого анализа, это живая история о том, почему мы отказались от идеи использовать Canvas, как строили карту с нуля, какие инструменты использовали, как организовали связь между SVG-элементами и данными в БД, и как сделали админку, где карту могут менять дизайнеры без участия разработчиков.

Как мы выбирали между Canvas и SVG

Итак, к нам пришли с задачей: заменить существующий модуль интерактивных карт офисов. Предыдущая система была сделана на Canvas, а координаты пинов рабочих мест хранились на бэкенде.

Это создавало проблему: любое изменение изображения карты офиса влекло риск несоответствия координат пина и самого рабочего места. Это был первый звоночек в пользу SVG — отрисовку пина можно привязать к конкретному объекту на карте, а не к абстрактным координатам.

Кроме того, информация на экране была перегружена. Ее имело смысл скрыть за всплывающими окнами, которые появлялись после выбора объекта пользователем. В SVG это сделать проще — каждый элемент доступен в DOM, и клик по нему обрабатывается нативно.

Более того, сами карты офиса предполагалось менять без участия разработчиков — силами дизайнеров и, в идеале, ответственными за офисную инфраструктуру. В крайнем случае — после короткого обучения персонала.

Этот пункт оказался критическим. Он отсеивает практически все популярные библиотеки для построения интерактивных карт, оставляя только вариант с использованием файлов SVG, нарисованных дизайнерами в привычных инструментах (Figma, Illustrator и т.п.).

Что должно быть интерактивным

По требованиям, взаимодействию с пользователем подлежат не все элементы, а только три типа:

  • Рабочие места

  • Переговорные комнаты

  • Оборудование 

У каждого типа — своя информация, которая отображается при клике.

В результате хотелось бы видеть что-то такое:

Но некоторая зависимость в отображении от BE все же присутствует, поговорим на примере оборудования.

Placeholder-элементы: как мы подставляли иконки в зависимости от данных бэка

В отображении оборудования есть зависимость от бэкенда — иконка должна меняться в зависимости от типа оборудования (принтер, массажное кресло, сканер и т.д.). Для этого мы использовали placeholder-элементы.

В исходном SVG-файле оставляем элементы-заглушки, на место которых после загрузки карты подставляется нужная SVG-иконка в зависимости от ответа бэкенда. Одна маленькая SVG на каждый тип оборудования.

Исходный файл SVG выглядит примерно так:

Логика «инициализации» интерактивных элементов применяется и к рабочим местам, и к переговорным, но в немного другом виде.

Здесь пришлось поиграться с отрисовкой через JS. Нам понадобились инструменты для определения трансформации placeholder’а и применения ее к новому элементу. Огромную помощь оказала библиотека svgdotjs — она значительно упростила работу с SVG.

После вставки нужного элемента на место мы помечаем его классом inited-equipment. Это позволяет селектировать его для CSS и JS. Аналогичный процесс проделываем для рабочих мест (inited-workplace) и переговорных (inited-room).

Поповеры: как отображать информацию рядом с элементом

Всю информацию об объектах мы скрыли в поповеры — как и планировали. Сделаны они динамическими компонентами Angular.

Главный вопрос: как отобразить поповер в нужном месте?

Изначально идея была использовать foreignObject — позиционировать его относительно кликнутого элемента. Но мы столкнулись с тем, что SVG обрезает элементы, выходящие за его viewBox. Пришлось искать другой путь.

В итоге мы реализовали поповер как абсолютно позиционированный элемент-сосед тега SVG самой карты. Позицию определяем через CTM (Current Transformation Matrix) элемента — оборудования, рабочего места или переговорной.

Мягкая связь между SVG и БД: самая сложная часть

Уже упоминалось, но стоит остановиться подробнее на «мягкой» связи между сущностью в БД и отображаемой сущностью в SVG.

Да, мы можем использовать id элементов SVG для идентификации элемента в БД (например, чтобы получать информацию о конкретном рабочем месте в поповере). Но удаление рабочего места из файла SVG магическим образом не удалит его из БД.

Это вынудило нас создать серьезную логику на загрузку новой карты.

Админка: редактирование и замена карт

Из-за описанной выше проблемы админка логически разделилась на два функционала:

  • Редактирование самой карты SVG.

  • Замена карты SVG за определенным офисом со своей валидацией загружаемого файла.

К счастью, функционал редактирования SVG хорошо закрывается проектом SVGedit (https://github.com/svg-edit/svgedit) с небольшими доработками.

Что мы доработали:

Ограничили инструментарий редактора, оставив только добавление конкретных объектов из так называемой «библиотеки элементов». Это упрощает добавление интерактивных элементов, об интерактивности которых должны знать и фронтенд, и бэкенд.

До готовности админки оставалось добавить функционал upload файла карты с полноценным парсингом и валидацией на бэкенде.

Валидация нужна, чтобы исключить удаление рабочих мест, за которыми закреплены сотрудники.

Жесткая валидация: запрещаем загрузку, если удален элемент с привязанным человеком относительно текущей сохраненной карты.

 

Мягкая валидация: разрешаем сохранение новой карты, если удалены объекты без закрепленных людей.

Ключевые выводы:

  • SVG — правильный выбор, когда важна работа с отдельными элементами и их интерактивность.

  • Placeholder-элементы — удобный способ подстановки иконок в зависимости от данных бэкенда.

  • svgdotjs — отличный помощник для работы с трансформациями и манипуляциями SVG.

  • Поповеры через абсолютное позиционирование — рабочий обход ограничений foreignObject.

  • Валидация загрузки карт — важна, если у вас есть связь между визуалом, представленным в виде файла карты офиса, и данными в БД, закрепленными за отображенными объектами в том файле.

  • SVGedit с ограничениями — хорошее решение для админки, если карты должен менять персонал, ответственный за инфраструктуру, а не разработчики.

Мы не претендуем на идеальность решения. Но таков был наш ответ на задачу интерактивной карты офиса.

А как бы вы решали эту задачу? Использовали бы готовые библиотеки или пошли своим путем? Сталкивались ли с похожими проблемами привязки данных к SVG-элементам?