d3-zoom
Панорамирование и масштабирование — популярные методы взаимодействия, которые позволяют пользователю сосредоточиться на области интереса, ограничивая представление. Их легко освоить благодаря непосредственному управлению: щелчок и перетаскивание для панорамирования (перевода), вращение колесика мыши для масштабирования (масштабирования) или использование касания. Панорамирование и масштабирование широко используются в веб-картах, но также могут применяться с визуализациями, такими как временные ряды и диаграммы рассеяния.
Поведение масштабирования, реализованное в d3-zoom, представляет собой удобную, но гибкую абстракцию для включения панорамирования и масштабирования на выборках. Оно обрабатывает удивительное разнообразие событий ввода и особенностей браузера. Поведение масштабирования не зависит от DOM, поэтому вы можете использовать его с SVG, HTML или Canvas.
Поведение масштабирования также разработано для работы с d3-scale и d3-axis; см. transform.rescaleX и transform.rescaleY. Вы также можете ограничить масштабирование с помощью zoom.scaleExtent и панорамирование с помощью zoom.translateExtent.
Поведение масштабирования можно комбинировать с другими поведениями, такими как d3-drag для перетаскивания и d3-brush для фокусировки + контекста.
Поведение масштабирования можно программно управлять с помощью zoom.transform, позволяя реализовывать элементы управления пользовательским интерфейсом, которые управляют отображением или подготавливать анимированные туры по данным. Плавные переходы при масштабировании основаны на статье «Гладкое и эффективное масштабирование и панорамирование» («Smooth and efficient zooming and panning») Джарке Дж. ван Вайка и Вима А.А. Нуйя.
См. также d3-tile для примеров панорамирования и масштабирования карт.
Установка
Если вы используете NPM, npm install d3-zoom. В противном случае скачайте последнюю версию. Вы также можете загрузить напрямую с d3js.org, либо как самостоятельную библиотеку, либо как часть D3 4.0. Поддерживаются среды AMD, CommonJS и vanilla. В vanilla экспортируется глобальная переменная d3:
<script src="https://d3js.org/d3-color.v1.min.js"></script> <script src="https://d3js.org/d3-dispatch.v1.min.js"></script> <script src="https://d3js.org/d3-ease.v1.min.js"></script> <script src="https://d3js.org/d3-interpolate.v1.min.js"></script> <script src="https://d3js.org/d3-selection.v1.min.js"></script> <script src="https://d3js.org/d3-timer.v1.min.js"></script> <script src="https://d3js.org/d3-transition.v1.min.js"></script> <script src="https://d3js.org/d3-drag.v1.min.js"></script> <script src="https://d3js.org/d3-zoom.v1.min.js"></script> <script> var zoom = d3.zoom(); </script>
Попробуйте d3-zoom в вашем браузере.
Справочник по API
В этой таблице описано, как поведение масштабирования интерпретирует события ввода:
| Событие | Элемент прослушивания | Событие масштабирования | Предотвращено по умолчанию? |
|---|---|---|---|
| mousedown⁵ | выборка | начало | нет¹ |
| mousemove² | окно¹ | масштабирование | да |
| mouseup² | окно¹ | конец | да |
| dragstart² | окно | - | да |
| selectstart² | окно | - | да |
| click³ | окно | - | да |
| dblclick | выборка | несколько⁶ | да |
| wheel⁸ | выборка | масштабирование⁷ | да |
| touchstart | выборка | несколько⁶ | нет⁴ |
| touchmove | выборка | масштабирование | да |
| touchend | выборка | конец | нет⁴ |
| touchcancel | выборка | конец | нет⁴ |
Распространение всех потребляемых событий немедленно останавливается.
¹ Необходимо для захвата событий вне iframe; см. d3-drag#9.
² Применяется только во время активного жеста с использованием мыши; см. d3-drag#9.
³ Применяется только сразу после некоторых жестов с использованием мыши; см. zoom.clickDistance.
⁴ Необходимо для разрешения эмуляции щелчков при вводе касанием; см. d3-drag#9.
⁵ Игнорируется, если находится в течение 500 мс после завершения жеста касания; предполагает эмуляцию щелчков.
⁶ Двойной щелчок и двойной тап инициируют переход, который генерирует события start, zoom и end.
⁷ Первое событие колесика вызывает событие start; событие end генерируется, когда в течение 150 мс не поступают события колесика.
⁸ Игнорируется, если уже достигнут соответствующий предел диапазона масштаба.
d3.zoom() Исходный код
Создает новое поведение масштабирования. Возвращаемое поведение, zoom, является и объектом, и функцией, и обычно применяется к выбранным элементам через selection.call.
zoom(selection) Исходный код
Применяет это поведение масштабирования к указанной выборке, привязывая необходимые обработчики событий для панорамирования и масштабирования и инициализируя преобразование масштабирования каждого выбранного элемента к тождественному преобразованию, если оно еще не определено. Эта функция обычно не вызывается напрямую, а вызывается через selection.call. Например, чтобы создать поведение масштабирования и применить его к выборке:
selection.call(d3.zoom().on("zoom", zoomed)); Внутри поведение масштабирования использует selection.on для привязки необходимых обработчиков событий для масштабирования. Обработчики используют имя .zoom, поэтому вы можете впоследствии отвязать поведение масштабирования следующим образом:
selection.on(".zoom", null); Чтобы отключить только масштабирование с помощью колесика мыши (например, чтобы не мешать стандартной прокрутке), вы можете удалить обработчик событий колесика мыши поведения масштабирования после применения его к выборке:
selection
.call(zoom)
.on("wheel.zoom", null); В качестве альтернативы используйте zoom.filter для большего контроля над тем, какие события могут инициировать жесты масштабирования.
Применение поведения масштабирования также устанавливает стиль -webkit-tap-highlight-color в прозрачный, отключая выделение при нажатии на iOS. Если вы хотите другой цвет выделения при нажатии, удалите или повторно примените этот стиль после применения поведения перетаскивания.
zoom.transform(selection, transform) Исходный код
Если selection — это выборка, устанавливает текущее преобразование масштабирования выбранных элементов в указанное transform, мгновенно генерируя события start, zoom и end события. Если selection — это переход, определяет «tween» масштабирования до указанного transform с использованием d3.interpolateZoom, генерируя событие start при запуске перехода, события zoom для каждого такта перехода и затем событие end при завершении перехода (или прерывании). transform может быть задан либо как преобразование масштабирования, либо как функция, возвращающая преобразование масштабирования. Если это функция, она вызывается для каждого выбранного элемента, передавая текущее значение данных d и индекс i, с контекстом this в качестве текущего DOM-элемента.
Эта функция обычно не вызывается напрямую, а вызывается через selection.call или transition.call. Например, чтобы мгновенно сбросить преобразование масштабирования до тождественного преобразования:
selection.call(zoom.transform, d3.zoomIdentity);
Чтобы плавно сбросить преобразование масштабирования до тождественного преобразования за 750 миллисекунд:
selection.transition().duration(750).call(zoom.transform, d3.zoomIdentity);
Этот метод требует, чтобы вы полностью задали новое преобразование масштабирования и не выполняет проверку заданного диапазона масштаба и диапазона сдвига, если таковые имеются. Для вывода нового преобразования из существующего преобразования и для проверки диапазонов масштаба и сдвига см. вспомогательные методы zoom.translateBy, zoom.scaleBy и zoom.scaleTo.
zoom.translateBy(selection, x, y) Исходный код
Если selection — это выборка, сдвигает текущее преобразование масштабирования выбранных элементов на x и y, таким образом, что новый tx1 = tx0 + kx и ty1 = ty0 + ky. Если selection — это переход, определяет «tween» сдвига текущего преобразования. Этот метод является вспомогательным методом для zoom.transform. Значения сдвига x и y могут быть заданы либо как числа, либо как функции, возвращающие числа. Если это функция, она вызывается для каждого выбранного элемента, передавая текущее значение данных d и индекс i, с контекстом this в качестве текущего DOM-элемента.
zoom.translateTo(selection, x, y) Исходный код
Если selection — это выборка, сдвигает текущее преобразование масштабирования выбранных элементов таким образом, что указанная позиция ⟨x,y⟩ появляется в центре диапазона представления. Новый tx = cx - kx и ty = cy - ky, где ⟨cx,cy⟩ — центр. Если selection — это переход, определяет «tween» сдвига текущего преобразования. Этот метод является вспомогательным методом для zoom.transform. Координаты x и y могут быть заданы либо как числа, либо как функции, возвращающие числа. Если это функция, она вызывается для каждого выбранного элемента, передавая текущее значение данных d и индекс i, с контекстом this в качестве текущего DOM-элемента.
zoom.scaleBy(selection, k) Source
Если selection — это выборка, то масштабирует текущее преобразование масштабирования выбранных элементов на k, таким образом, что новое k₁ = k₀k. Если selection — это переход, то определяет «масштабирование» анимацию, преобразующую текущее преобразование. Этот метод является удобным методом для zoom.transform. Коэффициент масштабирования k может быть указан как число или как функция, возвращающая число. Если это функция, она вызывается для каждого выбранного элемента, получая текущее значение данных d и индекс i, с контекстом this в качестве текущего DOM-элемента.
zoom.scaleTo(selection, k) Source
Если selection — это выборка, то масштабирует текущее преобразование масштабирования выбранных элементов до k, таким образом, что новое k₁ = k. Если selection — это переход, то определяет «масштабирование» анимацию, преобразующую текущее преобразование. Этот метод является удобным методом для zoom.transform. Коэффициент масштабирования k может быть указан как число или как функция, возвращающая число. Если это функция, она вызывается для каждого выбранного элемента, получая текущее значение данных d и индекс i, с контекстом this в качестве текущего DOM-элемента.
zoom.constrain([constrain]) Source
Если constrain указан, устанавливает функцию ограничения преобразования на заданную функцию и возвращает поведение масштабирования. Если constrain не указан, возвращает текущую функцию ограничения, по умолчанию:
function constrain(transform, extent, translateExtent) {
var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
return transform.translate(
dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
);
} Функция ограничения должна возвращать преобразование на основе текущего преобразования, диапазона видимой области и диапазона перемещения. Реализация по умолчанию пытается гарантировать, что диапазон видимой области не выходит за пределы диапазона перемещения.
zoom.filter([filter]) Source
Если filter указан, устанавливает фильтр на заданную функцию и возвращает поведение масштабирования. Если filter не указан, возвращает текущий фильтр, по умолчанию:
function filter() {
return !d3.event.button;
} Если фильтр возвращает ложное значение, инициирующее событие игнорируется, и никакие жесты масштабирования не начинаются. Таким образом, фильтр определяет, какие события ввода игнорируются. Фильтр по умолчанию игнорирует события mousedown на вторичных кнопках, поскольку эти кнопки обычно предназначены для других целей, таких как контекстное меню.
zoom.touchable([touchable]) Source
Если touchable указан, устанавливает детектор поддержки касаний на заданную функцию и возвращает поведение масштабирования. Если touchable не указан, возвращает текущий детектор поддержки касаний, по умолчанию:
function touchable() {
return "ontouchstart" in this;
} Обработчики событий касаний регистрируются только в том случае, если детектор возвращает истинное значение для соответствующего элемента, когда поведение масштабирования применяется. Детектор по умолчанию хорошо работает для большинства браузеров, способных к вводу касаний, но не для всех; например, эмулятор мобильного устройства Chrome не проходит проверку.
zoom.wheelDelta([delta]) Source
Если delta указан, устанавливает функцию дельты колеса мыши на заданную функцию и возвращает поведение масштабирования. Если delta не указан, возвращает текущую функцию дельты колеса мыши, по умолчанию:
function wheelDelta() {
return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1) / 500;
} Значение Δ, возвращаемое функцией дельты колеса мыши, определяет степень масштабирования в ответ на WheelEvent. Коэффициент масштаба transform.k умножается на 2Δ; например, Δ = +1 удваивает коэффициент масштаба, Δ = -1 делит коэффициент масштаба пополам.
zoom.extent([extent]) Source
Если extent указан, устанавливает диапазон видимой области на заданный массив точек [[x0, y0], [x1, y1]], где [x0, y0] — верхний левый угол видимой области, а [x1, y1] — нижний правый угол видимой области, и возвращает поведение масштабирования. Диапазон также может быть задан как функция, возвращающая такой массив; если это функция, она вызывается для каждого выбранного элемента, получая текущее значение данных d и индекс i, с контекстом this в качестве текущего DOM-элемента.
Если extent не указан, возвращает текущую функцию получения диапазона, по умолчанию [[0, 0], [ширина, высота]], где ширина — это ширина элемента, а высота — это его высота; для SVG-элементов используется ширина и высота ближайшего родительского SVG-элемента. В этом случае у родительского SVG-элемента должны быть определены атрибуты width и height вместо (например) свойств CSS или атрибута viewBox; SVG не предоставляет программного метода для получения начального размера видимой области. В качестве альтернативы, можно использовать element.getBoundingClientRect. (В Firefox, element.clientWidth и element.clientHeight равно нулю для SVG-элементов!)
Диапазон видимой области влияет на несколько функций: центр видимой области остается фиксированным во время изменений с помощью zoom.scaleBy и zoom.scaleTo; центр и размеры видимой области влияют на выбранный путь d3.interpolateZoom; и диапазон видимой области необходим для обеспечения необязательного диапазона перемещения.
zoom.scaleExtent([extent]) Source
Если extent указан, устанавливает диапазон масштабирования на заданный массив чисел [k0, k1], где k0 — минимальный допустимый коэффициент масштабирования, а k1 — максимальный допустимый коэффициент масштабирования, и возвращает это поведение масштабирования. Если extent не указан, возвращает текущий диапазон масштабирования, по умолчанию [0, ∞]. Диапазон масштабирования ограничивает масштабирование в и из. Он применяется при взаимодействии и при использовании zoom.scaleBy, zoom.scaleTo и zoom.translateBy; однако он не применяется при явном установлении преобразования с помощью zoom.transform.
Если пользователь пытается изменить масштаб прокруткой колесика мыши, когда уже достигнут соответствующий предел диапазона масштабирования, события прокрутки будут проигнорированы и не инициируют жест масштабирования. Это позволяет пользователю прокручивать вниз мимо масштабируемой области после масштабирования или прокручивать вверх после масштабирования. Если вы хотите всегда предотвращать прокрутку при вводе колеса мыши независимо от диапазона масштабирования, зарегистрируйте обработчик событий колеса мыши, чтобы предотвратить стандартное поведение браузера:
selection
.call(zoom)
.on("wheel", function() { d3.event.preventDefault(); }); zoom.translateExtent([extent]) Source
Если extent указан, устанавливает диапазон перемещения на заданный массив точек [[x0, y0], [x1, y1]], где [x0, y0] — верхний левый угол мира, а [x1, y1] — нижний правый угол мира, и возвращает это поведение масштабирования. Если extent не указан, возвращает текущий диапазон перемещения, по умолчанию [[-∞, -∞], [+∞, +∞]]. Диапазон перемещения ограничивает панорамирование и может вызвать перемещение при масштабировании. Он применяется при взаимодействии и при использовании zoom.scaleBy, zoom.scaleTo и zoom.translateBy; однако он не применяется при явном установлении преобразования с помощью zoom.transform.
zoom.clickDistance([distance]) Source
Если distance указан, устанавливает максимальное расстояние, на которое может перемещаться курсор между mousedown и mouseup, что вызовет последующее событие щелчка. Если в какой-то момент между mousedown и mouseup курсор находится на расстоянии большем или равном distance от своего положения в mousedown, событие щелчка, следующее за mouseup, будет подавлено. Если distance не указан, возвращает текущий порог расстояния, который по умолчанию равен нулю. Порог расстояния измеряется в клиентских координатах (event.clientX и event.clientY).
zoom.duration([duration]) Source
Если duration указан, устанавливает длительность анимаций масштабирования при двойном щелчке и двойном тапе на указанное количество миллисекунд и возвращает поведение масштабирования. Если duration не указан, возвращает текущую длительность, которая по умолчанию составляет 250 миллисекунд. Если длительность не больше нуля, двойной щелчок и двойной тап вызывают мгновенные изменения в преобразовании масштабирования, а не инициируют плавные переходы.
Чтобы отключить переходы при двойном щелчке и двойном тапе, можно удалить обработчик событий dblclick поведения масштабирования после применения поведения масштабирования к выборке:
selection
.call(zoom)
.on("dblclick.zoom", null); zoom.interpolate([interpolate]) Source
Если interpolate указано, устанавливает фабрику интерполяции для переходов масштабирования на указанную функцию. Если interpolate не указано, возвращает текущую фабрику интерполяции, которая по умолчанию равна d3.interpolateZoom для реализации плавного масштабирования. Для применения прямой интерполяции между двумя представлениями попробуйте использовать d3.interpolate вместо этого.
zoom.on(typenames[, listener]) Source
Если listener указан, устанавливает обработчик событий listener для указанных typenames и возвращает поведение масштабирования. Если обработчик событий уже был зарегистрирован для того же типа и имени, существующий обработчик удаляется перед добавлением нового. Если listener равен null, удаляет текущие обработчики событий для указанных typenames, если таковые имеются. Если listener не указан, возвращает первый из текущих назначенных обработчиков, соответствующий указанным typenames, если таковой имеется. При обработке указанного события каждый listener вызывается с тем же контекстом и аргументами, что и обработчики selection.on: текущее данное d и индекс i, с контекстом this в качестве текущего элемента DOM.
typenames — строка, содержащая один или несколько typename, разделенных пробелами. Каждый typename — это тип, необязательно за которым следует точка (.) и имя, например zoom.foo и zoom.bar; имя позволяет регистрировать несколько обработчиков для одного и того же типа. Тип должен быть одним из следующих:
-
start— после начала масштабирования (например, при нажатии мыши). -
zoom— после изменения преобразования масштабирования (например, при движении мыши). -
end— после завершения масштабирования (например, при отпускании мыши).
См. dispatch.on для получения дополнительной информации.
События масштабирования
При вызове обработчика событий масштабирования события масштабирования, d3.event устанавливается в текущее событие масштабирования. Объект события предоставляет несколько полей:
- event.target — связанное поведение масштабирования.
- event.type — строка «start», «zoom» или «end»; см. zoom.on.
- event.transform — текущее преобразование масштабирования.
- event.sourceEvent — базовое событие ввода, такое как mousemove или touchmove.
Преобразования масштабирования
Поведение масштабирования сохраняет состояние масштабирования в элементе, к которому было применено поведение масштабирования применено, а не в самом поведении масштабирования. Это связано с тем, что поведение масштабирования может быть применено к множеству элементов одновременно, и каждый элемент может быть отмасштабирован независимо. Состояние масштабирования может меняться как при взаимодействии пользователя, так и программно с помощью zoom.transform.
Чтобы получить состояние масштабирования, используйте event.transform в текущем событии масштабирования внутри обработчика событий масштабирования (см. zoom.on), или используйте d3.zoomTransform для данного узла. Последний вариант особенно полезен для изменения состояния масштабирования программно, например, для реализации кнопок для масштабирования в и из.
d3.zoomTransform(node) Source
Возвращает текущее преобразование для указанного node. Обратите внимание, что node обычно должен быть элементом DOM, а не selection. (Выбор может состоять из нескольких узлов в различных состояниях, и эта функция возвращает только одно преобразование.) Если у вас есть выбор, сначала вызовите selection.node:
var transform = d3.zoomTransform(selection.node());
В контексте обработчика событий node обычно является элементом, который получил событие ввода (который должен быть равен event.transform), this:
var transform = d3.zoomTransform(this);
Внутренне, преобразование элемента хранится как element.__zoom; однако, вы должны использовать этот метод, а не обращаться к нему напрямую. Если у данного node нет определенного преобразования, возвращает тождественное преобразование. Возвращаемое преобразование представляет собой двухмерную матрицу преобразования вида:
k 0 tx
0 k ty
0 0 1
(Эта матрица способна представлять только масштабирование и трансляцию; в будущих версиях может быть также разрешена ротация, хотя это, вероятно, не будет изменением, совместимым со старыми версиями.) Положение ⟨x,y⟩ преобразуется в ⟨xk + tx,yk + ty⟩. Объект преобразования предоставляет следующие свойства:
- transform.x — значение трансляции tx по оси x.
- transform.y — значение трансляции ty по оси y.
- transform.k — коэффициент масштабирования k.
Эти свойства следует рассматривать как только для чтения; вместо изменения преобразования используйте transform.scale и transform.translate для получения нового преобразования. Также см. zoom.scaleBy, zoom.scaleTo и zoom.translateBy для удобных методов поведения масштабирования. Чтобы создать преобразование с заданными k, tx и ty:
var t = d3.zoomIdentity.translate(x, y).scale(k);
Для применения преобразования к контексту 2D Canvas используйте context.translate, за которым следует context.scale:
context.translate(transform.x, transform.y); context.scale(transform.k, transform.k);
Аналогично, для применения преобразования к HTML-элементам с помощью CSS:
div.style("transform", "translate(" + transform.x + "px," + transform.y + "px) scale(" + transform.k + ")");
div.style("transform-origin", "0 0"); Для применения преобразования к SVG:
g.attr("transform", "translate(" + transform.x + "," + transform.y + ") scale(" + transform.k + ")"); Или проще, используя transform.toString:
g.attr("transform", transform); Обратите внимание, что порядок преобразований важен! Трансляция должна быть применена до масштабирования.
transform.scale(k) Source
Возвращает преобразование, масштаб k₁ которого равен k₀k, где k₀ — масштаб данного преобразования.
transform.translate(x, y) Source
Возвращает преобразование, трансляция tx1 и ty1 которого равна tx0 + x и ty0 + y, где tx0 и ty0 — трансляция данного преобразования.
transform.apply(point) Source
Возвращает преобразование указанной point, которая является массивом из двух чисел [x, y]. Возвращаемая точка равна [xk + tx, yk + ty].
transform.applyX(x) Source
Возвращает преобразование заданной x-координаты, xk + tx.
transform.applyY(y) Source
Возвращает преобразование заданной y-координаты, yk + ty.
transform.invert(point) Source
Возвращает обратное преобразование указанной point, которая является массивом из двух чисел [x, y]. Возвращаемая точка равна [(x - tx) / k, (y - ty) / k].
transform.invertX(x) Source
Возвращает обратное преобразование заданной x-координаты, (x - tx) / k.
transform.invertY(y) Source
Возвращает обратное преобразование заданной y-координаты, (y - ty) / k.
transform.rescaleX(x) Source
Возвращает копию непрерывной шкалы x, область значений которой преобразуется. Это реализуется путем применения обратного преобразования x к диапазону шкалы, а затем применения обратной шкалы для вычисления соответствующей области значений:
function rescaleX(x) {
var range = x.range().map(transform.invertX, transform),
domain = range.map(x.invert, x);
return x.copy().domain(domain);
} Шкала x должна использовать d3.interpolateNumber; не используйте continuous.rangeRound, так как это снижает точность continuous.invert и может привести к неточности преобразованной области значений. Этот метод не изменяет входную шкалу x; x, таким образом, представляет собой не преобразованную шкалу, в то время как возвращаемая шкала представляет ее преобразованный вид.
transform.rescaleY(y) Source
Возвращает копию непрерывной шкалы y, чья область значений преобразована. Это реализуется путём сначала применения обратного y-преобразования к области значений шкалы, а затем применения обратной шкалы для вычисления соответствующей области значений:
function rescaleY(y) {
var range = y.range().map(transform.invertY, transform),
domain = range.map(y.invert, y);
return y.copy().domain(domain);
} Шкала y должна использовать d3.interpolateNumber; не используйте continuous.rangeRound, так как это уменьшает точность continuous.invert и может привести к неточной преобразованной области значений. Этот метод не изменяет входную шкалу y; таким образом, y представляет собой не преобразованную шкалу, в то время как возвращаемая шкала представляет собой её преобразованный вид.
transform.toString() Source
Возвращает строку, представляющую SVG преобразование, соответствующее этому преобразованию. Реализовано как:
function toString() {
return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
} d3.zoomIdentity Source
Преобразование тождества, где k = 1, tx = ty = 0.
© 2010–2017 Michael Bostock
Licensed under the BSD License.
https://github.com/d3/d3-zoom