Разрабатываем библиотеку для отображения больших интерактивных схем залов на canvas без фреймворков и заставляем хорошо работать в ie и мобильных устройствах. Попутно разбираемся с особенностями работы canvas. Первым делом сформируем требования:
  Вспоминаем, что canvas по сути картинка с api, поэтому обработка ховеров и кликов на нашей совести: нужно самим считать координаты с учетом масштаба и скролла, искать объекты по их координатам. Но в тоже время мы полностью контролируем производительность и рисуем только то, что нужно. 

Постоянно перебирать все объекты на схеме и сверять их координаты не оптимально. Хотя это и будет происходить достаточно быстро, мы все равно сделаем лучше: построим деревья поиска, разбив карту на сектора.

Кроме оптимизации поиска, постараемся следовать следующим правилам работы с canvas:
 У браузера есть свой таймер отрисовки, и с помощью метода requestAnimationFrame можно попросить браузер отрисовать наш кадр вместе с остальными анимациями, —  это позволит избежать двойной работы браузера. Для отмены анимации есть cancelAnimationFrame. Полифил. Не обязательно постоянно перерисовывать сложные объекты, если они не изменяются. Можно отрисовать их заранее на скрытом canvas, а потом брать оттуда. Даже если элемент выходит за границы холста, на его отрисовку все равно тратится время.
Особенно это заметно в ie, он честно отрисовывает все, в то время в хроме это оптимизировано, и на это времени тратится намного меньше.
 Нет смысла перерисовывать всю сцену, если изменился один элемент. Отрисовка текста для canvas тяжелая задача, поэтому нужно избегать большого количества
объектов с текстом. Даже если хочется на каждое место поставить цифру — лучше ограничить отображение этой цифры масштабом: например, показывать цифру только при определенном приближении, когда эта информация будет полезна. 
 Scheme — основной класс.
View — класс знает canvas, на котором нужно рисовать, и его параметры (у нас их будет два).
SchemeObject — класс объекта схемы знает свое местоположение, как себя отрисовать и как обрабатывать события. Может содержать дополнительные параметры, например, цену.
EventManager — класс обработки и создания событий. При получении события передает его нужному классу.
ScrollManager — класс, отвечающий за скролл схемы.
ZoomManager — класс, отвечающий за зум схемы.
StorageManager — класс, отвечающий за хранение объектов схемы, создание дерева поиска и поиск объектов по координатам.
Polyfill — класс с набором поллифилов для кроссбраузерности.
Tools — класс с различными функциями, типа определения пересечения квадратов.
ImageStorage — класс создания канвасов для хранения изображений
 Очень хочется, чтобы у схемы были гибкие настройки. Для этого создадим такой нехитрый метод конфигурации объекта: Теперь можно конфигурировать объекты так: Это удобно: нужно только создать сеттеры у объектов, которые могут не просто установить значение в свойство, но и свалидировать или изменить значение при необходимости. Первым делом нужно научиться просто размещать объекты на схеме. Но для этого нужно понять, какие объекты сейчас находятся в зоне видимости. Мы договорились, не перебирать постоянно все объекты, а построить дерево поиска. Для построения дерева нужно разделять схему зала на части, записывать одну часть в левый узел дерева, а другую — в правый. Ключом узла будет являться прямоугольник, ограничивающий область схемы. Т.к. объект представляет плоскость, а не точку, он может оказаться сразу в нескольких узлах дерева — не страшно. Вопрос: как разбивать схему? Для достижения максимального профита, дерево должно быть сбалансировано, т.е. количество элементов в узлах должно быть примерно одинаковое. В нашем случае можно особо не заморачиваться, т.к. обычно объекты на схеме расположены практически равномерно. Просто делим пополам поочередно по ширине и высоте. Вот такое разбиение будет для дерева глубиной 8: Теперь нам очень просто найти желаемые объекты как по квадрату, так и по координатам. Здесь уже есть поправки на скролл и зум, про них чуть ниже поговорим. Еще мы можем легко определить, какие объекты лежат в зоне видимости и требуют отрисовки без перебора всех объектов: Класс хранения и поиска объектов: src/managers/StorageManager.ts Зум — это просто. У canvas есть метод scale, который трансформирует сетку координат. Но нам нужно не просто зумить, нам нужно зумить в точку, в которой находится курсор или центр.
 Для зума в точку нужно всего лишь знать две точки: старый центр зума (при старом масштабе) и новый, и добавить их разницу к смещению схемы: Но мы же хотим поддерживать тач устройства, поэтому нужно обработать движение двух пальцев и запретить нативный зум: В айфонах 6 и старше была найдена неприятная особенность: при быстром двойном касании возникал нативный зум с фокусом на канвасе, причем в таком режиме канвас начинал жутко тормозить. На viewport никакой реакции. Лечится так: Класс, отвечающий за масштабирование: src/managers/ZoomManager.ts Я решил просто прибавлять к координатам смещение слева и сверху.
Правда есть метод translate, который смещает сетку координат. На момент разработки он показался мне не очень удобным, но, возможно, я им еще воспользуюсь. Но это все мелочи, больше всего нас интересуют вопросы обработки событий.
 Некоторые люди при клике могут немного смещать курсор, это мы должны учесть:
 Класс, отвечающий за скролл: src/managers/ScrollManager.ts Вот вроде бы уже есть рабочий вариант схемы, но нас ждет неприятный сюрприз:
 наша схема сейчас быстро работает только в хроме. Проблема в том, что при перемещении схемы в полном размере и зуме из этого полного размера, перерисовываются все объекты. А когда уже в масштабе помещается только часть объектов — работает нормально. Сначала я хотел объединить ближайшие места в кластеры, чтобы место сотни объектов рисовать один при мелком масштабе. Но не смог найти/придумать алгоритм, который бы делал это за разумное время и был бы устойчивым, т.к. объекты на карте могут быть расположены как угодно. Потом я вспомнил правило, которое написано на каждом заборе (и в начале этой статьи) при работе с canvas: не перерисовывать неизменяющиеся части. Действительно, при перемещении и зуме сама схема не изменяется, поэтому нам нужно просто иметь «снимок» схемы в n раз больше начального масштаба и, при перемещении/зуме не рендерить объекты, а просто подставлять нашу картинку, пока разрешение карты не превысило разрешение снимка. А потом уже и оставшиеся реальные объекты будут быстро рисоваться в виду своего количества. Но эта картинка тоже должна иногда меняться. Например, при выборе места оно меняет вид и мы не хотим, чтобы выбранные места исчезали на время перемещения схемы. Перерисовывать весь снимок (в n раз больше начального размера карты) при клике дорого, 
 но в тоже время мы можем позволить себе на снимке не сильно заботиться о пересечении объектов и обновлять только квадрат, в котором находиться измененный объект.  Таким вроде бы нехитрым способом мы очень сильно повысили скорость работы схемы.  Спасибо, что дочитали до конца. В процессе работы над схемой я подглядывал в исходники fabricjs и chartjs чтобы меньше велосипедить. Программист