Автор оригинала: FreeCodeCamp Community Member.
Ларс Верпугл
Как увеличить масштаб и сковороду в визуализациях данных с использованием SVG и Canvas
Лучший абзац открытия для статьи Zoom D3 уже был написан, и он идет так:
Это хорошо. Четыре предложения, он говорит вам именно то, что увеличивается и то, что он делает, и, вероятно, что более важно – это забирает ваши огромные страхи.
Итак, все это было сказано тогда? Ну, это никогда не имеет. Всегда хорошо иметь многочисленные разные перспективы, особенно с событиями, которые перемещают ваши драгоценные визуальные наглядные по всему магазину и масштабируют его по усмотрению вашего Trigger-Happy.
Некоторое время назад я работал над довольно сложной визуализацией со многими движущимися элементами и длинным списком взаимодействий, в том числе зума и кастрюля, на его первоначально темном сердце. Статический визуальный сам был уже относительно сложным, но добавляя масштабирование и кастрюлю к нему почувствовало немного, как связывая мой сын 4 на 6 футов замка Lego на бегущей водой буйвола.
Концептуальная проблема здесь такая зум и кастрюля, поэтому принципиально мешают нашей работе. Похоже, они контролируют довольно много нашей визуализации ручной работы, что редко когда-либо одно целое, но осторожное сочетание позиций, масштабов и осей. Это может быть запутано в лучшем случае и запугиваться в худшем случае.
Итак, после моего масштабирования и сковороды ходили в доверие, и были проверены в нескольких других проектах, время казалось, созрела, чтобы написать их. Может быть, уже слишком поздно, и вы все взломали его годы назад, но даже тогда это может быть полезно иметь другую перспективу.
Для нашего путешествия будет три части:
- Синхронный рецепт для увеличения и сковороды
- Строительство визуально
- Реализация зума и кастрюля в SVG и в холсте
Как бонус, мы добавим программное увеличение и сделайте нашу визуальную красивую.
Теперь вы можете посмотреть на эту полосу прокрутки там, думая, что вы пропустите ужин при чтении всего этого. Это подробно описано по причине, но мне позволит вам просмотреть, а выбирать вишня, когда я указываю разделы, вы можете перепрыгнуть, не пропавшие не пропустили решающие вещи. Таким образом, вы можете сделать эту поездку как короткую или тщательную, как вам нравится, и извлечь что-то из-за этого.
Простой зум и панорамический рецепт
Эта первая часть – позвоночник этого поста. Это короткое руководство – не более, чем серия из пяти простых точек, которые вы можете следовать при наращивании вашего зума и событий Pan. Это руководство даст вам последовательность жизни, как интегрировать Zoom и Pan в ваше приложение. Асинхронный и завязанный мир программирования часто помогают серию синхронных и простых шагов.
Согласование некоторой терминологии
Перед тем, как мы потянемся вдоль линии, давайте сначала определим некоторую полезную терминологию:
- А увеличить преобразование это объект, создаваемый и поддерживаемый D3. Это ваше самое ценное владение в контексте увеличения и панорамирования, и он содержит три значения: х и y Перевод, а также масштабный фактор, представленный к Отказ Мы увидим, когда и где он производится и изменился очень скоро. Вот как это выглядит в своем первоначальном состоянии:
- Он говорит: «Пользователь еще не увеличен или не высказал визуальный. Следовательно, коэффициент масштабирования масштабирования составляет 1 и перевод x и y равно 0. “
- увеличить поведение Это система событий, которая отслеживает и передает значения преобразования. Потребляет слушатель (принимает к сведению) действия пользователя. После активации он отправит объект события с информацией об этом событии в функцию обработчика. Вы напишите этот обработчик и используете информацию об объекте события. Наиболее важной частью информации, которую ваш обработчик зума будет получен вышеперечисленное преобразование при каждом увеличении активности. Все, что мы хотим сделать с ценностями преобразования, мы сделаем в обработчике зума. Это может звучать как много, но в самой простой форме вы устанавливаете поведение масштабирования, как так:
var zoom = d3.zoom().on('zoom', zoomed);- zoom base является родительским элементом зума прикреплен или зарегистрировано на , так как они сказали. Это делает две вещи: 1) Это поверхность, которая принимает во всех ходах и жестах пользователя, а 2) он содержит объект преобразования ( X , y, и масштабный фактор к ).
- Zoom Targets Все ли элементы, которые мы хотим передвигаться. Если вы хотите увеличить и выйти из круга, то этот круг будет вашим целевым зуме.
Кроме того, мы могли бы выбрать различать два типа зума. Они станут намного яснее, когда мы перейдем к нашим примерам, но это будет полезно определить их на верхнем уровне первым:
- Геометрический зум (или Графический зум ) означает элементы просто масштабируются вверх или вниз без каких-либо различий. Все их свойства будут расшифрованы вверх или вниз. Подумайте об этом как о движении или масштабирования системы координат соответствующих элементов. Все на нем будет масштабировано и перемещено без разбора. Геометрический зум ближе всего к нашему реальному опыту. Когда мы идем к дому, каждый аспект дома выглядит больше на каждом шаге. В равной степени, если мы масштабируем ось, все части она станут больше или меньше – линии, доменной путь, этикетки. Например, этикетка оси 14PX, масштабированная по шкале 2, появится 14 × большой.
- Семантический зум (или неемеометрический зум ) означает, что мы контролируем свойство каждого элемента во время увеличения. Если у нас есть ось, например, с этикетками размера 14px и мы семантически масштабируем к оси, мы могли бы командовать метками, чтобы сохранить их оригинальный размер для каждого масштаба. Линии могут стать более крупными и тоньше, а ось будет перемещена в соответствии с увеличением масштабирования, но наша этикетка оставалась бы большая 14px.
Мы не будем прикоснуться к этому ниже, но добросовестное семантическое зум может идти дальше. Это позволяет нам не только контролировать свойства элемента, но представление нашего элемента в зависимости от уровня зума. Google Maps Например, показывает страны при увеличении, штатах или административных районах в среднем увеличении и меньших городах при увеличении.
Zoom и Pan 5 шагов
Мы хорошо оборудованы для этого сейчас. Вот наш зум и сковороду в пяти простых шагах:
1 Создайте свой статический визуальный
Чтобы увеличить наглядность, вам понадобится визуальный.
2 Определите базу масштабирования и масштабирования целей зума
Возьмите кусок бумаги, выдвинуйте элемент, который слушает ( Zoom Base ), и запишите список элементов, которые должны перемещаться (The Zoom Targets ).
- Выберите свой zoom base элемент сначала. Определите, какой элемент DOM вы хотите использовать для базы зума. Вы можете прикрепить масштаб к
SVGСG,rectили любой другой элемент, к которому ваша мышь имеет доступ. Обратите внимание, чтоGЭлементы могут только зарегистрировать события, где у них есть дети с заполнением. Итак, если у вас есть большойGЭлемент с круглым радиусом 1, ваши жесты зума будут работать только на этом крошечном круге. Это часто лучше всего настроить выделенный прямоугольник SVG (Reble) с заполнением, но 0 непрозрачностью иУказатели-событияустановитьВсезарегистрировать прослушиватель Zoom. Возможно, вам придется расстроить указательные события восходящих элементов. - Определите свой Увеличить цель элементы и запишите их вниз. Помните, что цели зума являются элементами, которые вы хотите перемещать. Сделайте список всех элементов целевого зума.
- Для каждой цели определите, если вы хотите использовать геометрический или семантический зум Отказ
- Обратите внимание на это. Вот пример таблицы вы можете в конечном итоге с:
3 Настройте поведение масштабирования
Теперь вы хотите настроить поведение, которое сделает слушатель слушать.
- Создайте поведение масштабирования по крайней мере:
var zoom = d3.zoom().on('zoom', zoomed);- Проверьте D3 API Ссылка для D3.ZOOM () Для помощников методов, таких как
ScaleeExtentиtranslateextentОтказ - Позвоните в поведение масштабирования на вашем базовом элементе, как:
zoomBaseElement.call(zoom)
ПРИМЕЧАНИЕ, вам не нужно называть базой Zoom ZoombaseElelement конечно.
4 Напишите обработчик
Вот где будет возникать зум и кастрюль. Одноразовое владение Handler будет трансформировать Обновление объекта х , у, и к непрерывно, когда пользовательские колеса или перетаскивания. Вы будете применять их к вашему зумам целям.
- Первое, что вы хотите сделать, это захватить
трансформироватьОбъект передан в обработчик слушателем при каждом взаимодействии пользователя (колесо или мышь):
var transform = d3.event.transform;
- Теперь, когда у вас есть ваши параметры зума и панорамирования ( TX , вытеснять С к ), вы можете сделать все, что вы хотите с этим …
- Если вы хотите только администрировать Геометрический зум , вы просто звоните:
zoomTargetElement .attr('transform', 'translate(' + transform.x + ', ' + transform.y + ') scale(' + transform.k + ')');или проще:
zoomTargetElement.attr('transform', transform.toString());… что точно так же. Это предполагает, что вы хотите применить все значения преобразования. Вы также можете сосредоточиться только на театр , TY или масштаб к конечно.
- Если вы хотите семантический зум Вам нужно вскаскаться.
- Предполагая, что все ваши значения данных прошли через масштаб, чтобы переводиться от данных на экран пространства, этот перевод меняется на Zoom. Если ваша точка данных была переведена на пробел в пикселе 50 перед увеличением масштабирования, увеличение будет перемещать его в другую точку.
- Если вы переведете X на 5 и масштаб на 2, новая позиция будет:
- К счастью, вам не нужно (и не должно) не производить эти расчеты самостоятельно, но вы можете пересматривать масштаб на каждом увеличении и применить его к целевым свойствам, которые вы хотите изменить. К ним относятся оси или круги или
Рексили какие бы целевые формы и компоненты у вас есть. - Со шкалой под названием
Xscale, вы можете использовать функцию сахара.reescalex. ()и применить это так:
var updatedScale = transform.rescaleX(xScale);
- Теперь вы можете использовать
Обновленная местностьВ вашей увеличенной функции для всех элементов, которые вы хотите обновить. Например, ось:
xAxis.scale(updatedScale); gAxis.call(xAxis);
- или набор положений круга х:
circles.attr('cx'. function(d) { return updatedScale(d.value); })5 Вам нужно программно переместить свою цель в положение?
- Рассчитать/определить положение и масштаб
- Определить новые позиции TX и вытеснять и новый масштаб к в собственном D3
трансформироватьФункция производства, говоря:
var t = d3.zoomIdentity.translateBy(tx, ty).scale(k);
- Храните объект в базе Zoom и распространяйте изменения, вызывая свой первый обработчик зума, который будет перемещать цели:
zoomBaseElement.call(zoom.transform, t);
- Теперь включить Zoom Zoom с:
zoomBaseElement.call(zoom)
Вот, пожалуйста. Я думаю, что они называют это главным резюме. Тем не менее, мы только поверхностно коснулись ключевых концепций и даже не упомянули различные рендерры. Давайте добавим некоторую плоть к костям с настоящим примером.
Что мы делаем
Вот морская свинка, которую мы построим в прохождение:
Это визуализация планет нашей солнечной системы, показывая их расстояние от Солнца. Масштабирование пригодится, чтобы позволить обзор, и панорамирование передает некоторые чувства для расстояния. Кроме того, все шары розовые!
О, и вам не нужно, но если хочешь, вы можете следовать. Иди сюда для всех комментарий кода Отказ Кроме того, вы можете просто играть с приложением шаг за шагом Отказ Я брошу ссылку всякий раз, когда мы прогрессируем.
Создание нашего статического визуального
Как и в случае почти всех визуализаций, данные – наша отправная точка. Итак, вот в полном объеме:
У нас есть 8 планет, 1 звезда под названием Солнце, и Плутона, что на самом деле больше не планета, но все еще здесь для романтических причин. У нас также есть расстояние каждой планеты от Солнца и их радиусов. Это все, что нам нужно. Но для того, чтобы превратить его в это:
… Нам нужно написать какой-нибудь код.
Обратите внимание: этот пост о масштабировании, а не о создании статической визуализации нашей солнечной системы. Тем не менее, я проберусь по коду, чтобы дать вам туристическую поездку этого приложения. Однако, если вы здесь только для увеличения масштабирования, пожалуйста, не стесняйтесь просматривать этот раздел и быстро перейти к первому этапу зум-здания под названием «Определение нашего масштабирования и целевых целей увеличения масштабирования» (ума, возможно, стоит прочитать расчет Размеры в разрезе в мгновение).
Начнем с редкого HTML:
Measuring our planets' distances
Вот и все. У нас есть заголовок со ссылкой и промежуток Чтобы дать ему подходящую розовую нижнюю границу и контейнер Div для нашего зрения. Теперь мы будем двигаться быстро на JavaScript и обходив CSS, который не приглашен до конца этого поста …
Первое, что мы делаем, это загрузить данные:
d3.csv('planets.csv', row, function(error, data) { if (error) throw error; make(data); });
function row(d) { return { planet: d.planet, distance: +d.distance, radius: +d.radius }; }Мы загружаем в нашу Planets.csv трубы его через ряд () Функция, которая гарантирует, что наши номера действительно номера. Тогда мы называем Сделать () Функция, которая будет домом всего дополнительного кода.
Сделать () Функция делает следующее:
- Это устанавливает размеры нашего визуального
- Это строит
SVGа также поверхность зума - Это рассчитывает наши масштабы
- Это строит нашу ось
- Это строит планеты
Давайте начнем с установки размеров наших визуальных средств.
Расчет измерений ^
Маржа и вычисления высоты простыны:
var margin = { top: window.innerHeight * 0.3, left: 50, bottom: window.innerHeight * 0.4, right: 50 }; var height = window.innerHeight - margin.top - margin.bottom;
Мы хотим SVG элемент, чтобы покрыть весь наш экран. Так что наш рост будет window.innerheight вычитание некоторых полей. Мы определяем верхнюю и нижнюю маржу в отношении window.innerheight держать их относительно друг к другу.
На ширину, которая нуждается в том, что просто немного подумать:
var maxDist = d3.max(data, function(d) { return d.distance; }); var mapScale = 1/10e4;
// The full width of all planets var chartWidth = maxDist * mapScale;
// svg width will only be as large as screen var screenWidth = window.innerWidth - margin.left - margin.right;
Гист нашего расчета ширины в том, что мы хотим Два ширина. Один для диаграммы и один для SVG Отказ Какая разница? Ну, график будет очень широкой, потому что она должна соответствовать всем нашим планетам на нем. SVG Однако, однако, не нужно быть очень широким. SVG Задание – показать нам планеты, которые соответствуют нашему окну браузера. SVG Размеры окна отныне достаточно. Это будет выглядеть так:
Обратите внимание, что это возможно только с использованием поведения масштабирования. Если бы мы хотели позволить пользователю видеть все планеты без магии Zoom и Pan, нам нужно иметь SVG так же широко, как график. В результате браузер даст нам полосы прокрутки нашим пользователям, которые могут использовать для перемещения вправо или влево – в чудесных Если луна было всего 1 пиксель визуальный
Однако, используя ZOOM D3, объект преобразования зума, который мы инициализируем, будет следить за нашими жестами: насколько далеко мы «прокручиваем» вправо, влево, и вдоль оси z практически проникают через экран, следуя нашей линии.
На основании преобразования мы можем повторно расположить наши элементы. И если они случаются в пределах экранных координат, они отображаются на нашей базе SVG Отказ Нет вреда, если нет, они просто не будут показаться.
Как таковой, наше SVG ширина получит Screenwidth который просто window.innerwidth минус поля. Насколько широкой будет наше ChartWidth База для всех планет, быть? Мы будем по масштабам расстояния между двумя самым дальним апартаментом (солнцем и плутором, то есть) с нашей Mapscale на 10E4 или 1: 10000. Когда Pluto составляет 5,913 000 000 км от солнца в реальном пространстве, он будет 59 13 13 пикселей от центра Солнца в нашем визуальном.
Это было не так уж плохо. Далее!
Сделать базу
Во-первых, мы строим нашу SVG База: маржинальная трансформированная G Элемент свисает от SVG элемент:
var svg = d3.select('#vis') .append('svg') .attr('width', screenWidth + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('class', 'chart') .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');Затем мы накладываем это с rect элемент, который мы будем использовать как наш zoom base Отказ Это rect Послушаю все события мыши и жесты, а как таковые мы смело называем это Слушай дарит :
var listenerRect = svg .append('rect') .attr('class', 'listener-rect') .attr('x', 0) .attr('y', -margin.top) .attr('width', screenWidth) .attr('height', height) .style('opacity', 0);Важно отметить здесь, что наш масштабирующий базой находится на том же месте, что и целевые значения зума – элементы, которые мы хотим увеличить. Мы приложим наш масштабирующий база Слушай дарит к SVG (который на самом деле маржа переведен Г.Чарт Элемент, как вы можете увидеть только один код кода выше), который также станет домом наших кругов планеты, мы нарисованным позже.
Весы дальше.
Настройка наших весов
Мы отображаем две меры для координат экрана: расстояние и радиус. Как таковые нам нужны две весы. Вот первое, отображение радиусов нашей планеты в км к экранному радиусу:
var rExtent = d3.extent(data, function(d) { return d.radius; }); var rScale = d3.scaleLinear() .domain([0, rExtent[1]]) .range([3, height/2 * 0.9]);
Во-первых, мы получаем масштаб радиуса. Мы рассчитываем домен и сопоставьте эти значения в диапазоне 3PX до чуть менее половины высоты нашего окна, сохраняя меры относительно окна.
Наше второе масштаб – это масштаб расстояния:
var xScale = d3.scaleLinear() .domain([0, maxDist]) .range([0, chartWidth]);
Мы рассмотрим данные о данных в полной мере ChartWidth Отказ Если вы сопоставили его на Screenwidth Все планеты будут стоять на ногах:
Мы могли бы исправить это, используя более жесткий уровень радиуса, но мы хотели бы, чтобы они были изначально растягиваться, а затем позволяют пользователю увеличить или выходить.
Рисование оси
Мы будем использовать нормальный компонент Axis D3 для создания оси. Однако, как вы можете видеть на изображении выше, мы будем расторгать этикетки, чтобы они не перекрывались.
Во-первых, мы строим компонент оси:
var xAxis = d3.axisBottom(xScale) .tickSizeOuter(0) .tickPadding(10) .tickValues(data.map(function(el) { return el.distance; })) .tickFormat(function(d, i) { return data[i].planet + ' ' + d3.format(',')(d) + ' km'; });Мы определяем точное количество ярлыков галочек, передавая массив значения расстояния планетов в .tickvalues. () :
[0, 58000000, 108000000, 150000000, 228000000, 778000000, 1429000000, 2871000000, 4504000000, 5913000000]
Ось теперь будет опираться только на этикетки тика для этих значений. Мы используем .tickformat. () Указать, что скажет метка. В нашем случае это будет <имя планеты> <Расстояние от Sun>
Теперь мы производим ось ‘ G База и развязать компонент на нем:
var xAxisDraw = svg.insert('g', ':first-child') .attr('class', 'x axis') .call(xAxis);Как наше Слушай дарит Ось становится ребенком нашего G.CHART Элемент мы пометили овладение Отказ Зачем вставлять это? Мы хотим, чтобы наш масштабированный масштабировать быть на вершине всех других элементов, болтающихся от овладение Так что это может потреблять все события. Глядя на Дом, это должен быть последний ребенок SVG Отказ Для этого мы вставим ось – и вскоре планеты – до Слушай дарит Отказ
Переезд на нашу оси этикетки. По умолчанию все этикетки будут нарисованы на том же y уровень. Но мы хотим, чтобы они пошатали, поэтому нам нужно написать какой-то код для достижения шагов. Это Стэмгер Вуду, мы применяем:
// Move the axis-labels and -lines down
var labelHeight = xAxisDraw.select('text').node().getBBox().height; xAxisDraw.attr('transform', 'translate(0, ' + (height + labelHeight * data.length) + ')'); // Position the axis text
xAxisDraw.selectAll('text') .attr('y', function(d, i) { return -(i * labelHeight + labelHeight); }) .attr('dx', '-0.15em') .attr('dy', '1.15em') .style('text-anchor', 'start');Не чувствую себя обязанным следовать за мной на эту кроличью дыру – короче мы перемещаем их все вниз # этикеток × Высота их этикетки Отказ Затем мы перемещаем каждую метку по Их рост × Их индекс Отказ В результате солнце, например, не поднимается, так как он будет поднят 0. ×. , но Меркурий (следующая планета на солнце) будет двигаться по 1 × Лэйчхейт и так далее.
Линия клещей нужна немного больше внимания, так как мы должны удовлетворить его Y1 и Y2 стоимость:
// Draw the axis lines
xAxisDraw.selectAll('line') .attr('y1', function(d, i) { return -(i * labelHeight + labelHeight); }) .attr('y2', function(d, i) { return -(i * labelHeight + labelHeight + from axis-y 0 // ^ this label's start position (data.length-1-i) * labelHeight + // ^ the distance from the start position // to the bottom of the chart area height); // ^ the height });Хорошие новости. Теперь мы можем нарисовать наши планеты в (почти) одной цепочке D3:
var gPlanets = svg .insert('g', '.listener-rect') .attr('class', 'planet-group');var planets = gPlanets.selectAll('.planet') .data(data) .enter().append('circle') .attr('class', 'planet') .attr('id', function(d) { return d.planet; }) .attr('cx', function(d) { return xScale(d.distance); }) .attr('cy', 0) .attr('r', function(d) { d.scaledRadius = rScale(d.radius); return d.scaledRadius; });Во-первых, мы создаем группу для всех наших планет и убедитесь, что Слушай дарит Также охватывает эти планеты, вставляя наши G.Planet-Group до rect.listener-rect Отказ Тогда мы присоединяемся и Enter () данные для нашего еще виртуального .planet , который будет проявляться как круги с соответственно масштабированными расстояниями как х позиции и rscale D Radii. Здесь:
Большой! У нас есть наши визуальные. Теперь давайте доберемся до увеличения …
Идентификация нашего масштабирования базы и масштабирования целей ^
Часто мудрое идею начать, думая о том, что вы хотите сделать до головы первого кода. Перед настройкой нашего зум давайте идентифицируемся что и Как Мы хотим увеличить масштаб и кастрюлю. Мы задаем 3 вопроса:
- Что будет нашему масштабированию – «датчик элемента», который мы будем использовать для увеличения?
- Что будет нашим целям зума – элементы, которые мы будем двигаться?
- Какой тип зума мы хотим для каждого элемента – геометрический или семантический зум?
Определение нашего масштабирования
Давайте сначала выберем наш базовый элемент масштабирования. Вы можете прикрепить масштаб к SVG С G , rect или любой другой элемент, к которому ваша мышь имеет доступ. Обратите внимание, что G Элементы могут только зарегистрировать события, где у них есть дети с набором заполнить свойство. Итак, если у вас есть большой G Элемент с круглым радиусом 1, ваши жесты зума будут работать только на этом крошечном круге.
Как таковой, часто разумно настроить посвященный rect с заполнением, но 0 непрозрачностью. Вы должны убедиться, что база Zoom может потреблять все события. Итак, он должен быть на вершине всех других элементов или его Указатели-события должно быть установлено на Все Пока все другие элементы ‘ Указатели-события установлены на Нет Отказ
На самом деле, мы уже полностью решили настроить дополнительные rect элемент для прослушивания событий. Мы мудро кэшировали это в Слушай дарит Переменная, которую мы можем ссылаться на настройку. Готово.
Определение наших целей зума
Теперь давайте определим наши целевые элементы и запишите их. Какие элементы мы хотим двигаться, когда мы увеличим масштаб и кастрюлю? Давайте сделаем список:
- Планеты
- Ось и все их элементы (только клещевые линии и текст галочки; мы не показываем путь оси).
Теперь мы знаем нашу масштабирующую базу и наши цели, мы хотим убедиться, что они делятся одной и той же системой координат на начальном состоянии масштабирования – когда не произошло Zoom или Pan. Вот почему мы прикрепили масштабную базу и цели (планеты, ось) для того же G выше.
Это идет очень хорошо!
Определение типа зума
Наконец, давайте решим Как Мы хотим их масштабировать – геометрически или семантически? Прежде всего, это различие имеет смысл для масштабирования, а не панорамирования. Мы определили его выше, но с целью избыточной полноты, давайте повторим, что геометрический зум простой: все элементы просто масштабируются вверх или равномерно. Семантический зум – это немного более сложный, как вы можете решить, что вы хотите увеличить или вниз.
В нашем случае мы можем захотеть масштабировать размер планет, но держать ширину линии в 4PX. Для этого нам нужен семантический зум. Для наших образовательных целей давайте реализуем оба типа! Почему бы нет?
Настройка зума
Для любого увеличения мы решаем реализовать, нам нужно будет сначала настроить его. Вы, вероятно, согласитесь, что это не может быть менее сложным:
var zoom = d3.zoom() .on('zoom', zoomed);Призыв d3.zoom () вернет объект и функцию. Как и во многих частях API D3, объект позволяет настроить переменные, которые мы используем в функции. Так что мы поднимаемся, настроить использование d3.zoom () Функция с одним методом: .на () Прикрепляет функцию обработчика под названием увеличен Отказ увеличен будет называться каждый раз, когда мы zoom Отказ Это где мы сделаем элементы перемещения.
У нас есть два других события цикла зума для запуска функции, Начать и конец Отказ Это должно быть относительно легко догадаться, когда они будут вызвать обратный вызов.
Мы храним возвращенную функцию в творчески называемой переменной zoom Отказ Далее мы можем использовать эту функцию AS Zoom (<слушатель-элемент T>) или, как это чаще сделано в d3 <слушатель-элемент> ; .call (Zoom) вроде так:
listenerRect.call(zoom);
Это здорово, но что это значит? Это означает, что Слушай дарит теперь официальный дом нашего зума. Наше zoom base Действительно В этот самый момент у него есть две вещи, болтающиеся от него: .на () событие и преобразование зума. Если мы console.dir (d3.select ('# listener-rect'). Узел ()) И проверьте наши атрибуты, мы найдем эти два свойства D3 в самом нижней части списка:
__on Объект содержит нашу информацию слушателя, а __ЗМ Объект – это объект преобразования, содержащий 3 значения, которые мы обсуждали в начале этого горшка: х и y Перевод, когда мы увеличиваем и сковороду, и масштабный фактор к меняется на зуме.
Вы всегда можете прийти к базе увеличения – Слушай дарит Для нас – запросить текущие значения преобразования. Однако вам не нужно делать это очень часто, так как преобразование будет возможности доступно в объекте события из нашего увеличен Функция обработчика. Правильно. За любовь к нашей жизни – давайте, наконец, увеличить.
Геометрический зум с SVG
У нас есть наш статический визуальный. Мы настроили зум. Мы прикрепили его к базе увеличения. Давайте, наконец, решим, какой тип увеличения мы собираемся. Вот вещь: оси должны быть увеличены семантически, вы решаете для других элементов. Возвращаясь к нашему зумам целям, давайте укажем это здесь в таблице на части пергамента:
Теперь, давайте напишем обработчик зума:
function zoomed() { var transform = d3.event.transform;
gPlanets.attr('transform', transform.toString()); }
Мы еще не совсем сделаны, но это самое простое увеличение возможно и уже будет перемещать наши планеты. Мы кэшируем объект преобразования, который болтает D3.Event Объект, который проходит на каждом увеличении и кастрюле в переменной трансформировать Отказ Затем мы перемещаем наши планеты, просто обновляя трансформировать атрибут наших кругов.
Transform.tostring () Это просто удобный метод, который дает нам объект преобразования. Это экономит нас от того, чтобы набрать значение атрибута преобразования. Для преобразования личности {k: 1, x: 0, y: 0} Возвращает строку «Перевод (0, 0) масштаб (1)»
Как это будет выглядеть?
Очень хороший! Планеты движутся – остальное нет. Нам нужно сделать 3 вещи, чтобы улучшить это:
- Давайте запретим планетам переехать вправо (не осталось ни планеты от солнца, поэтому было бы бесполезно).
- Давайте также запретим планеты от движения вверх и вниз.
- Переместить весы.
1 и 2 просты; Мы просто манипулируем объектом преобразования, прежде чем использовать его так:
function zoomed() { var transform = d3.event.transform; transform.x = Math.min(0, transform.x); transform.y = 0;
gPlanets.attr('transform', transform.toString()); }
В результате х никогда не выше 0, и поэтому мы не можем переместить вещь справа. Также y всегда будет 0. Результат делает то, что мы ожидаем:
Далее давайте сделаем осье оси семантически. Наша ось состоит из ярлыков и линий. Мы выбираем семантический над геометрическим зумом, так как мы только хотим изменить их позиция На зум – не размер метки или ширина линии.
Основной двигатель позиционирования за элементами оси – вещь, которая делает этикетки и линиевые движения – это масштаб. И что делает масштаб? Шкала отображает наши значения данных на ширину нашего SVG элемент. Если мы хотим изменить масштаб с D3, мы обычно обновляем домен шкалы и/или диапазон. Но по мере окончания осей по окончании рекакции такая общая активность для увеличения D3, у нас есть Rescalex () и Rescaley () Методы болтались с трансформировать объект. Обновляет сопоставление для нас в соответствии с увеличением. Идеальный синтаксический сахар Мы можем использовать для создания обновленной шкалы:
var xScaleNew = transform.rescaleX(xScale);
Следующий раздел называется Семантический зум с SVG И небрежно открыть капот этого Rescalex () метод гораздо более подробно. Но на данный момент, давайте просто использовать xscalenew доверительно, как так:
xAxis.scale(xScaleNew); xAxisDraw.call(xAxis);
Мы обновляем масштаб нашего xaxis и перерисовать ось с нашей новой осью компонента. Последнее, что нам нужно сделать с осью, – это снова наши этикетки и линии, как мы сделали выше.
// Stagger the axis-labels xAxisDraw.selectAll('text') .attr('y', function(d, i) { return -(i * labelHeight + labelHeight); }) // Stagger the axis-lines xAxisDraw.selectAll('line') .attr('y1', function(d, i) { return -(i * labelHeight + labelHeight); }) .attr('y2', function(d, i) { return -(i * labelHeight + labelHeight + (data.length-1-i) * labelHeight + height); });Помните, все это происходит в нашем увеличен обработчик.
Оно работает:
Семантический зум с SVG
Этот заголовок приходит немного поздно. У нас уже семантически увеличилась наша ось. Но теперь давайте также применим его к нашим планетам и погрузившись в процесс рекалирования. Вот наша обновленная подготовительная таблица:
Семантический зум кругов
Прежде всего, почему мы хотим использовать семантический зум на планетах? Я думаю, что вышеупомянутая Гиф демонстрирует семантическую потребность довольно хорошо. Поскольку планеты становятся меньше, их контур почти невозможно увидеть. С семантическим зумом у нас будет контроль над тем, какие свойства элемента меняются или остаются. В нашем случае Zoom следует изменить положение, а также размер наших планет, но ширина наброска должна оставаться постоянными в 4PX.
Что мы делаем, просто:
function zoomed() { var transform = d3.event.transform;
transform.x = Math.min(0, transform.x);
var xScaleNew = transform.rescaleX(xScale);
planets .attr('cx', function(d) { return xScaleNew(d.distance); }) .attr('r', function(d) { return d.scaledRadius * transform.k; }); // Zoom and pan the axis here (…)
}
Во-первых, мы удаляем нашу геометрическую планету увеличить. Затем мы возьмем наши планеты и, вместо того, чтобы преобразовывать их, мы специально доступны только к их усадьба и R атрибуты. х позиция будет заново рассчитана с обновленным xscalenew И радиус просто должен быть умножен на масштабный фактор. Здесь не требуется перевод.
И это все:
Однако далеко мы увеличиваем, наш инсульт остается в 4PX, позволяя нам на самом деле увидеть наши планеты, даже если они полностью уменьшаются.
Понимание Zoom Rescale
Семантический зум требует от нас, чтобы увеличить и свойства панорамирования выборочно. Наша семантическая планета Zoom выше только изменила CX и R атрибут, сохраняя ширину хода в 4PX. Специально изменить усадьба , нам нужно было обновить нашу шкалу – основной позиционирующий двигатель нашей визуализации – так что он позиционирует наши элементы в соответствии с новым преобразованием.
Как сказано, D3 предлагает удобные методы Rescalex () и Rescaley () обновлять весы в соответствии с преобразованием. Конечно, это совершенно нормально, чтобы использовать эти методы, не зная внутреннюю работу, поэтому, пожалуйста, не стесняйтесь прыгать прямо к Следующий раздел Отказ Но если вам интересно, как именно появляется вскакан, оставайся со мной. Также будут изображения в цвете.
Мы будем использовать реальный простой пример. Давайте предположим, что мы смотрим только на X-размерность, и мы хотим сопоставить пространство данных, которое охватывает домен от 0 до 100 до 1000 пикселей широкого экрана. Таким образом, у нас есть домен данных [0, 100], мы хотим отобразить диапазон ширины [0, 1000]. Наш масштаб выглядел бы так:
var xScale = d3.scaleLinear() .domain([0, 100]) .range([0, 1000]);
Давайте также предположим, что у нас есть один круг с значением 20 данных, который будет сопоставлен на значение 200:
Легко. Теперь мы увеличиваем, так что наш масштабный фактор к будет 2. Нет перевода, просто увеличить. В результате наш круг будет двигаться в соответствии с нашим формулой преобразования зума, мы начали этот пост с: tx + x × k , что бы привело к 0 + 200 × :
Примечание, мы также расширили его радиус на 2. Все хорошо до сих пор? Большой.
В этом случае мы могли бы просто сделать наш расчет преобразования для круга. Но это намного проще, удобнее и более последовательно продолжать использовать нашу масштаб. Тем не менее, нам нужно обновить его, так как наше значение 10 данных не должно больше масштабироваться до 100 пикселей, кроме до 200px!
как нам это сделать? Как мы уже сделали выше, мы просто проходим наш Xscale к transform.reescalex () функция. Это возвращает соответственно обновленные newxscale , который мы используем в значении данных круга, чтобы определить CX позиция:
var newXScale = transform.rescaleX(xScale);
circle.attr('cx', function(d) { return d.dataValue; }); // note: d.dataValue is from a fictitious datasetНо что именно делает эта вскакание? Давайте посмотрим на источник Код Во-первых, прежде чем рассмотреть свою логику. Вскакание под капотом выглядит так:
function rescaleX(x) { var range = x.range().map(transform.invertX, transform),
domain = range.map(x.invert, x);
return x.copy().domain(domain);
}
Как вы можете видеть в последней строке, это вернет оригинальный масштаб Но с обновленным доменом. Диапазон останется как есть. Если вы спросили меня, прежде чем я посмотрел на этот код, я бы догадался, D3 будет обновлять диапазон и сохранить домен как есть. Гораздо более прямой. Но это наоборот. Это имеет смысл, поскольку диапазон пикселей является более статической концепцией. В нашем случае 1000 – ширина экрана – это не изменится на Zoom.
(Малый) недостаток состоит в том, что новый расчет домена немного больше участвует, чем будет вычисление нового диапазона. Существует 4 шага, участвующих в расчете нового домена в каждом увеличении и движении PAN:
- Сначала мы возьмем диапазон нашего оригинального масштаба. В нашем примере это будет [0, 1000].
- Затем мы применим обратную трансформацию к нему, что вернется [0, 500].
- Далее мы будем использовать масштаб
.invert.invert. ()Способ найти значения данных, связанного с значениями диапазона 0 и 500, который будет [0 и 50] в нашем случае. - Наконец, мы переопределяем текущий домен X-Scale с этим новым доменом и верните его.
Но почему? Давайте рассмотрим это концептуально …
Во-первых, мы рассчитываем новый диапазон, взяв обратную функцию нашего преобразования для х стоимость. К настоящему времени мы знаем функцию преобразования зума для x tx + x × k Отказ Его обратный это (X – TX)/к Отказ
Если вы никогда не сталкиваетесь с обратными функциями, они только наоборот – обратная сторона их основной функции. Если у вас было f (x) × x Тогда обратный это g (y)/3 Отказ Подключение 2 в главной функции f (x) Возвращает 6 – подключение этого 6 в обратной функции G (Y) Возвращает 2 снова. Это меняет процесс основной функции.
Почему мы берем обратное в нашем диапазоне? Мы хотим отрегулировать домен, но держите диапазон в [0, 1000]. Самый простой способ получить обновленный домен – это сначала рассчитать обновленные значения диапазона (мин и макс), чтобы извлечь из них значения нового домена.
Давайте сыграем это через одно значение. Давайте возьмем наш максимальный уровень диапазона 1000. Наши текущие масштабы отображают максимальное значение данных 100 до максимального значения диапазона 1000 пикселей.
Какое значение максимального диапазона, когда мы масштабируем на 2? Масштабирование на 2 означает, что мы увеличиваем. Итак, наш текущий максимальный диапазон 1000 будет перейти к 2000 (0 + 1000 × 2) Отказ Тем не менее, мы хотели бы узнать новую точку пикселей, которая перемещается к краю нашего экрана, когда мы увеличиваем. Предыдущий момент, который был в 1000 и сейчас в 2000 году, не помогите нам, так как выходит за рамки экрана. Итак, какой момент находится на краю нашего окна после увеличения насмешек? Какой момент наш новый максимальный диапазон?
Чтобы получить этот момент, мы не спрашиваем: где наша текущая максимальная стоимость диапазона 1000 Zoom? Мы спрашиваем, откуда приходит новый максимальный диапазон! Логично, это противоположное или Обратная вопрос. Соответственно, мы применяем обратное преобразование зума: (X – TX)/квалификация Отказ Подключаем наш предыдущий максимальный диапазон 1000px, наш TX 0 и масштабирования к из 2, чтобы получить: (1000-0)/ Отказ
Теперь мы можем сказать, что наше новое значение максимального диапазона придет из положения 500 пикселей.
Почему мы сделали это снова? Разве это не все немного глупо, как мы хотим держать диапазон в [0, 1000] в любом случае? да. И нет. Это не глупо, потому что мы не используем это новое максимальное значение диапазона в новом входе диапазона для нашей масштаба. Мы просто используем его, чтобы найти наше новое максимальное значение домена данных.
Мы берем наш оригинальный шкал, который сопоставил значение данных от 0 до 0 пикселей, значение данных от 100 до 1000 пикселей и все между значениями соответственно. Теперь мы спрашиваем, какие значения данных отображаются в пиксельную стоимость 500? Для этого простого случая мы можем использовать наш мозг или – намного лучше – мы используем .invert () Способ нашего оригинального X-шкалы. xscale.invert (500) вернет 50, как и ожидалось, ожидается.
Давайте помним здесь, что у нас все еще есть наш оригинальный ассортимент [0, 1000]. Все расчеты, которые мы сделали, были сделаны только для того, чтобы добраться до нового домена. Наше новое X-Scale по-прежнему отображает значение данных 0 до пикселя 0, но теперь отображает новое значение домена максимального данного значения 50 к значению максимального диапазона Loyally 1000.
Аналогичным образом, наше значение X Centre X все еще имеет значение данных 10, которое сейчас не отображает до 100, но до 200. Мы успешно увеличились, мы сделали.
Отличная работа! Теперь, далее для холста. Та же игра – другая доска …
Геометрический зум с холстом ^
У нас есть только 10 кругов на нашем сайте. Тем не менее, есть, конечно, есть большие более шары, чтобы визуализировать. Визуализация более 1000 из них может привести вас к проблемам оказания производительности, которые вы можете попытаться вылечить с холстом.
В отличие от SVG, Canvas производит одно растровое изображение вашего чертежа. 1000 планет на вашем экране будут нарисованы к одному элементу DOM, холст Отказ В SVG 1000 планеты будут производить 1000 элементов круга, которые браузер должен поддерживать, что влияет на производительность. Есть список ресурсов холста в Источники Раздел ниже Если вы хотите узнать больше, но не волнуйтесь, вам не нужна степень холста, чтобы следовать.
Мы изменим очень мало в нашем приложении. Как быстрое напоминание, вот основные шаги, которые мы следили, чтобы получить здесь:
- Загрузить данные
- Рассчитайте размеры нашего визуального
- Создайте базу SVG и прямоугольник слушателя
- Рассчитайте весы
- Определите и нарисуйте ось
- Построить SVG Visual
- Zoom.
Мы изменим точки 3, 6 и 7 выше и оставьте остальные без изменений. Фактически, мы не будем производить чистый холст, а скорее нарисут планеты в холсте и сохраняют оси в SVG. Это называется Визуализация смешанного режима, И действительно умно, если у вас есть оси, чтобы нарисовать. Оси рисунки чудесно решаются D3 в SVG, но могут быть болью в холсте. ( Ilijah meks посвящает хороший раздел в рендеринг смешанного режима в главе 11 его книги D3JS в действии )
Добавление базы холста
Как и в случае SVG, нам нужна база для нарисования. Для холста нам нужны две вещи, холст Элемент и его контекст чертежа – инструменты, которые мы можем использовать для рисования на холсте. Ниже нашего SVG Основание мы добавляем следующий фрагмент базы холста:
var canvas = d3.select('#vis').append('canvas') .attr('width', screenWidth + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom); var context = canvas.node().getContext('2d');Часто разумно пропустить маржинальную конвенцию для холста (у нас нет г Мы можем двигаться вокруг). Но, Особенно при рисовании осей SVG мы хотим цепляться на нашу маржу.
Мы также хотим наложить наши холст элемент идеально над нашим SVG Элемент и его дети, планета G и Слушай дарит Отказ Для этого нам нужно дать ему тот же размер, что и SVG Элемент и положите холст абсолют на вершине SVG Отказ Вот наши CSS:
canvas { position: absolute; top: 0; left: 0; pointer-events: none; }Обратите внимание, что мы также удалим все указатели из наших холст так что Слушай дарит получает все жесты. В результате у нас довольно много слоев:
G Теперь только удерживает нашу ось, которую мы можем просматривать нашу SVG Отказ холст Покажет наши планеты, но только раздел зеленый выше (другие планеты нарисованы здесь для полноты, но изначально будут невидимыми). Верхний уровень – Слушай дарит потребляя все мероприятия указателя и информирование нашего зума и кастрюля.
Рисование кругов планеты в холсте
Мы снимаем логику, которая построила планеты SVG, а вместо этого нарисовать наши круги холста. Мы нарисуем его в одной функции. Позвольте мне сначала показать вам код этой функции рисования холста, прежде чем запустить его через него. Вот так:
function drawGeometricCircles(data, transform) {Мы передаем наши данные и преобразование. Если мы только хотели построить статические визуальные, нам не нужно беспокоиться о трансляциях, но масштабирование очень наша миссия!
context.clearRect(0, 0, screenWidth + margin.left + margin.right, height + margin.top + margin.bottom);
Далее мы получаем доступ к нашему контексту HONVAS (мы кэшировали в Context переменную) и запустить метод под названием .CLEARRECT. Отказ Вы, безусловно, догадаетесь, что это делает - это очищает холст Отказ Мы передаем это холст Размеры, которые очистит холст Каждый раз, когда мы называем эту функцию.
Это то, что мы делаем с холстом. В отличие от SVG, где у нас есть умелые узлы в доме для наших кругов, у нас есть только пиксельный образ на нашем холст Отказ Вместо того, чтобы двигаться вокруг узла DOM, мы просто удаляем изображение, которое мы нарисовали раньше и нарисовать новое изображение с элементами в несколько разных позициях. Это холст для вас.
context.save();
Тогда мы .спасти () По умолчанию и неизменном контексте, и мы .восстановить () Это через момент после того, как все рисунок делается. Таким образом, мы обеспечиваем не только пустой холст, но и пустой контекст, когда мы нарисуем новую планету.
context.lineWidth = 4; context.strokeStyle = 'deeppink'; context.fillStyle = 'white';
Далее мы определяем нашу живопись кисти. Мы хотим ширину линии 4 часа, мы хотим, что цвет инсульта глубоководства и заполнения белого цвета. Эти эстетические свойства будут применяться ко всему, что мы рисуем после их установки. Пока мы не изменим их.
context.translate(transform.x + margin.left, margin.top); context.scale(transform.k, transform.k);
Эти следующие две линии являются геометрическим зумом. Мы переводим и масштабируем все изображение, которое мы рисуем соответствующими значениями преобразования.
for (var i = 0; i < data.length; i++) {context.beginPath(); context.arc(xScale(data[i].distance), 0, rScale(data[i].radius), 0, 2 * Math.PI, false); context.stroke(); context.fill();
context.fill();
}
context.restore();
}
Наконец, мы рисуем круги. Если вы еще не видели большую часть холста, это может выглядеть немного сырое. И действительно, D3 вновь активизирует эту петлю через элементы для нас, присоединившись к данным к выборам, которые мы можем впоследствии доступны, положение и стиль.
С холстом мы делаем это сами. Мы зацикливаем данные, начните путь, нарисуйте путь как круг с context.arc () Метод и, наконец, инсульт и заполните путь.
Остальная часть кода. Нам просто нужно назвать это прямо здесь, а затем с нашими данными и личности, трансформироваться, который просто {k: 1, x: 0, y: 0} :
drawGeometricCircles(data, d3.zoomIdentity);
Всякий раз, когда мы увеличиваем, мы заменяем код, который переместил наши планеты SVG с этим:
drawGeometricCircles(data, transform);
Я избавлю тебе gif, как это выглядит точно так же, как то, что мы видели выше с геометрическим зум SVG. Но рабочая реализация с кодом просто щелчок отеля Действительно
Семантический зум с холстом
Давайте отпразднуем наш геометрический масштабный подвиг, избавившись от него. На самом деле, чтобы добиться семантического вместо геометрического зума, мы просто переименяем и изменим нашу функцию рисования. Мы назовем это соответствующим образом DrawsemanticCirles () Отказ
Изменение из геометрического до семантического масштабирования в холсте требует тех же действий высокого уровня. Вместо перевода и масштабирования системы координат планеты мы изменим позиции и радиус планеты в соответствии с преобразованиями.
DrawsemanticCirles () очистит наш холст, а затем нарисуйте все круги с HookCircle () :
function drawSemanticCircles(data, transform) { context.clearRect(0, 0, screenWidth + margin.left + margin.right, height + margin.top + margin.bottom);
for (var i = 0; i < data.length; i++) { drawCircle(data[i], transform); } }
HookCircle () Будут запускать для каждого элемента данных, принимая элемент данных и текущее преобразование:
function drawCircle(elem, transform) { var x = (transform.x + transform.k * xScale(elem.distance)) + margin.left; var y = margin.top; var r = transform.k * rScale(elem.radius);
context.lineWidth = 4; context.strokeStyle = 'deeppink'; context.fillStyle = 'white';
context.beginPath(); context.arc(x, y, r, 0, 2 * Math.PI); context.stroke(); context.fill();
}
Сначала мы определяем х и y Позиции, а также радиус R Отказ Затем мы определяем стили для наших кругов. Наконец, мы рисуем наши галактические сферы как дуги. И это все…
Большой! Мы покрывали два типа масштабирования двух визуализации. На бонусных треках: программное увеличение и создание нашей галактики.
Программное увеличение
Часто полезно перемещать наши визуальные эффекты в определенную позицию. Вы можете позволить пользователю центру карту, переместить длинную гистограмму в начало или увеличить и выходить из солнечной системы.
У нас нет ни карта, ни гистограммы, поэтому давайте программно уменьшаемся и обратно на наши планеты при нагрузке. Мы вернемся к SVG для этого, так как нам не нужно холст здесь. Из-за его более низкого уровня, я бы порекомендовал использовать холст, только если вам это нужно или поговорить, как ваш родной язык. Как у нас есть только 10 кругов, чтобы двигаться здесь, нам это не нужно.
Вот что мы хотим достичь:
Начнем с сильно увеличенного наглядывания в масштабе масштабирования 20. Затем мы уменьшаем наш минимальный зум, поэтому все планеты комфортно соответствуют на странице. Наконец, мы увеличиваем масштаб к нашему масштабу масштабирования по умолчанию 1.
Чтобы добиться этого, мы болтаем на программной логике к нижней части нашего Сделать () Функция, где все наш код приложения живет. Начнем с увеличения масштабированного коэффициента 20 без панорамирования:
var initialTransform = d3.zoomIdentity.scale(20); listenerRect.call(zoom.transform, initialTransform);
d3.zoomidentity Возвращает преобразование личности, мы уже столкнулись несколько раз. Мы меняем масштаб преобразования до 20 и кэшируйте его в initialtransform. Отказ Тогда мы используем zoom.transform () функция. Эта функция, очевидно, отличается от нашего объекта преобразования, но он напрямую манипулирует его. Мы используем это здесь с собственным <Выбор>. Способ вызова (), который мы столкнулись выше. Выбор мы Звонить Zoom.trans Форма () на будет ее первый аргумент. Это будет наше зум база слушать ошибочно, домой для нашего текущего объекта преобразования. Второй аргумент должен быть новым объектом преобразования. Это заменит текущее преобразование на этот узел.
Вишня сверху – это то, что вместо того, чтобы пройти наш масштабировать основание в виде простого выбора, мы можем передать его как переход. Помните (или примечание), что переходы просто производные выборы, поэтому передача в isustraft.transition () на самом деле будет переходить нашим визуальным из одного преобразования к другому.
Но до сих пор мы только что подняли наши визуальные в масштабе 20. Давайте запустим переход. Сначала в масштабе минз Мы определили ранее, затем в масштабе 1. Вот что мы делаем:
// Trigger programmatic zoom progZoom()
Давайте напишем это. Это не примет никаких аргументов:
function progZoom() {Сначала мы определим преобразование для минз Мы хотим увеличить в первую очередь:
var zoomOutTransform = d3.zoomIdentity.scale(minZoom);
В следующих строках мы превращаем наше Слушай дарит в переход и позвонить Zoomtransform () опять таки. Использование .call () Мы проходим в переходе, который мы просто построили в качестве первого аргумента и Zoomeouttransform – Minzoom Преобразование мы только что спасли:
listenerRect .transition() .duration(5000) .call(zoom.transform, zoomOutTransform) .on('end', zoomToNormal)В конце увеличения мы называем функцию под названием Zoomtonormal Отказ Это делает именно то, что мы только что сделали, помимо перехода-масштабирования до преобразования идентичности:
function zoomToNormal() { listenerRect .transition() .duration(3000) .ease(d3.easeQuadInOut) .call(zoom.transform, d3.zoomIdentity) }Помимо масштабирования до другого преобразования, мы также устанавливаем различную продолжительность, а также другую функцию ослабления.
}
И это был наш первый бонусный трек. На отслеживание двух …
Сделать наши визуальные
Это мудро получить ваши визуальные эффекты прямо в черно-белых и белых (розовых и белых в нашем случае). Но в конце концов, лиз краски не может повредить. Чтобы добраться сюда …
… нам нужно только изменить несколько вещей, из которых свечение планеты, вероятно, является наиболее сложной. Давайте сначала посмотрим на остальные:
Мы добавим темно-синюю предпосылку с радиальным градиентом, движущимся в темно-синий от слегка легкой. Это одна линия в нашем Тело CSS:
body { font-family: Avenir, sans-serif; sans-serif; font-size: 0.75rem; margin: 0; background: radial-gradient(#091C33, #091426); }Мы изменяем текст и цвет линии на серый белый ( #DDD ), а вместо твердых линий Мы визуализируем пунктирные линии с широкими пробелами:
.tick line, .lines { stroke: #ddd; stroke-width: 0.5; shape-rendering: crispEdges; stroke-dasharray: 1,5; }Наконец, мы заполняем планеты нашим любимым DeepPink и добавьте свечение. Свечение – это фильтр SVG, мы применяем к каждой планете. Я не буду здесь подробно рассказать, но вы можете найти код комментировать прямо здесь Отказ Короче говоря, мы немного уклоняем планеты, прежде чем опереться их гауссовским размытом. Мы заполняем размытие DeepPink и умарать в результате света. Фильтр получает идентификатор # Soft-Glow , какие наши планеты могут ссылаться с Фильтр атрибут:
var planets = gPlanets.selectAll('.planet') .data(data) .enter().append('circle') .attr('class', 'planet') // (…) .attr('filter', 'url(#soft-glow)');И это все!
Мы пришли долгим образом, и, надеюсь, вы теперь понимаете D3 Zoom немного лучше. Мы смотрели в короткий рецепт, вы можете следовать до и во время проводки вашего визуального изображения с любым масштабированием и панорамированием. Затем мы применили этот план на реальный проект с розовыми шарами, играя через геометрический и семантический рендеринг зума в SVG, а также на холсте. Как бонус, мы смотрели на программное зум и, наконец, сделали его тонко розовое лицо еще розовым. Как весело!
Еще две вещи, которые могут помочь: быстрое замечание на обновлении вашего увеличения от D3 V3 до V4 и списком источников.
Обновление увеличения от V3 до V4
В 2016 году (как во многих поколениях назад) D3 V4 заменил V3 с некоторыми великими, но нарушения изменения. Некоторые концептуальные изменения, включая поведение масштабирования, сохраняли Devs ночью (включая себя). Изменения являются последовательными и разумными, но стоят немногих дополнительных заметок, которые могут помочь вам найти сон:
- Как и с V3, Zoom in v4 представляет собой только перевод X и Y и масштабами – параметрами преобразования. То есть, конечно же, жестоко упрощая сложность, но это мантра, которую вы должны попробовать, когда объединены.
- Параметры преобразования хранятся с базой масштабирования в V4, в то время как они хранились с поведением в V3. Поведение теперь просто передает преобразование к целям. Это приятно знать, когда мы хотим получить преобразование вне обработчика зума.
- Поведение V3 автоматически восстановило масштаб. В V4 вам необходимо увеличить масштаб в функции масштабирования вручную и обновлять все формы и компоненты на основе масштаба. Это немного больше работы, но значительно меньше магии и более четкое разделение опасений.
Источники ^ ^
Там нет обилия D3 (v4) zoom, связанных с посты и учебниками. Там отсутствие было по одной из причин написать это руководство. Тем не менее, есть несколько зум-драгоценных камней, а также некоторые полезные дальше возможности, связанные с холстом, которые вы можете взглянуть на:
Дополнения статьи:
- REPO GitHub для всех кода мы прошли в этой статье
- Все шаги, которые мы взяли выше, как рабочие приложения с кодом
Учебники увеличения:
Zoom Tech:
Холст:
Я действительно надеюсь, что вам понравилось читать это. Пожалуйста, хлопайте, если вы хотите распространить слово, следуйте за мной на Twitter и Скажи привет Либо просто сказать привет или расскажи мне о других способах увеличения.
Знания являются частичными, и мы все здесь, чтобы узнать …
Первоначально опубликовано www.datamake.io .