Интерактивное масштабирование и перемещение сцены в SVG-визуализациях
строится вокруг системы жестов и преобразований, реализуемой в D3.js
через модуль d3.zoom(). Этот механизм объединяет обработку
колесика мыши, перетаскивания и сенсорных жестов в единый поток
трансформаций, который применяется к графическому контейнеру.
Система зума опирается на три ключевых компонента:
Каждое взаимодействие пользователя преобразуется в объект трансформации:
transform = { k, x, y }
где:
k — коэффициент масштабированияx, y — смещение по осямНачальное состояние задаётся через:
const zoom = d3.zoom();
После создания поведение привязывается к SVG-элементу:
d3.select("svg")
.call(zoom);
Основной механизм работы строится вокруг события zoom,
которое вызывается при любом изменении трансформации:
zoom.on("zoom", (event) => {
d3.select("g")
.attr("transform", event.transform);
});
Объект event.transform содержит актуальное состояние
сцены. Применение трансформации к группе <g>
позволяет перемещать и масштабировать всю визуализацию как единое
целое.
Начальная трансформация представляется как:
d3.zoomIdentity
Она эквивалентна:
k = 1, x = 0, y = 0
Принудительная установка начального масштаба выполняется через:
svg.call(zoom.transform, d3.zoomIdentity);
Или с изменёнными параметрами:
svg.call(zoom.transform, d3.zoomIdentity.translate(100, 50).scale(2));
По умолчанию d3.zoom() обрабатывает событие wheel.
Внутренний алгоритм:
Управление диапазоном масштабирования задаётся через:
zoom.scaleExtent([1, 8]);
Минимальное значение предотвращает “отдаление” сцены до бесконечности, максимальное ограничивает детализацию.
Перемещение сцены регулируется через:
zoom.translateExtent([
[0, 0],
[width, height]
]);
Эта настройка ограничивает область панорамирования, предотвращая уход графики за пределы заданного прямоугольника.
Отдельно существует extent, задающий зону
реагирования:
zoom.extent([
[0, 0],
[width, height]
]);
Изменение состояния без пользовательского ввода выполняется через вызов transform:
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity.scale(3).translate(50, 20));
Такой подход используется для анимации переходов между уровнями детализации.
<g>Типичная структура SVG:
<svg>
<g class="viewport">
<!-- графические элементы -->
</g>
</svg>
Применение transform:
zoom.on("zoom", (event) => {
d3.select(".viewport")
.attr("transform", event.transform);
});
Такой подход отделяет координатную систему сцены от внешнего SVG-контейнера.
Любое изменение масштаба влияет на интерпретацию координат. Для обратного преобразования используется:
const inverted = event.transform.invert([x, y]);
Это позволяет сопоставлять экранные координаты с координатами сцены.
Математически преобразование описывается:
x' = kx + tx
y' = ky + ty
где:
k — масштабtx, ty — сдвигКаждое событие zoom содержит:
event.transformevent.sourceEventevent.typeТипы источников:
Разделение источников позволяет адаптировать поведение под тип устройства.
Фильтрация событий выполняется через:
zoom.filter((event) => {
return !event.ctrlKey;
});
Это позволяет отключить масштабирование при определённых модификаторах клавиатуры.
Дополнительно можно ограничивать поведение:
zoom.scaleExtent([1, 5]).filter(event => event.type !== "dblclick");
Параметр scaleExtent влияет на диапазон, но не на
скорость. Для изменения чувствительности используется:
zoom.scaleBy(selection, factor);
или:
zoom.scaleTo(selection, k);
Эти методы изменяют масштаб относительно текущего состояния.
Один экземпляр zoom может применяться к нескольким контейнерам:
svg1.call(zoom);
svg2.call(zoom);
Общее состояние поддерживается через ручное распространение transform:
zoom.on("zoom", (event) => {
d3.selectAll(".viewport")
.attr("transform", event.transform);
});
При использовании шкал d3.scaleLinear и других
масштабируемых функций необходимо учитывать трансформацию:
const newX = event.transform.rescaleX(xScale);
Это позволяет синхронизировать оси с текущим зумом без ручного пересчёта координат.
Применение:
xAxis.call(d3.axisBottom(event.transform.rescaleX(xScale)));
Внутренне transform представляется как матрица:
[k, 0, 0,
0, k, 0,
x, y, 1]
Это обеспечивает совместимость с SVG transform-цепочками и позволяет комбинировать масштабирование с поворотами и смещениями.
На сенсорных устройствах zoom автоматически интерпретирует:
kx, yПри этом сохраняется центр между двумя пальцами как точка фокуса масштабирования.
Сброс состояния выполняется через:
svg.call(zoom.transform, d3.zoomIdentity);
Частичное восстановление:
svg.call(zoom.translateBy, 50, 50);
или
svg.call(zoom.scaleBy, 1.2);
При большом количестве элементов zoom часто комбинируется с:
В таких сценариях трансформация применяется не к DOM-элементам, а к контексту отрисовки.
Наиболее частые проблемы:
<g>Корректная архитектура всегда предполагает единый источник
трансформации — event.transform.
Типовой цикл работы zoom: