Рубрики
Без рубрики

D3 и холст в 3 шага

LARS VERSPOGL D3 и HONVAS в 3 stesthe Bind, ничья и взаимодействие, говорят, что вы создаете визуализацию данных с использованием D3 и SVG. Вы можете попасть на потолок, когда вы пытаетесь отобразить несколько тысяч элементов одновременно. Ваш браузер может начать слоеть

Автор оригинала: FreeCodeCamp Community Member.

Ларс Верпугл

Связывание, ничья и интерактивность

Допустим, вы создаете визуализацию данных, используя D3 и SVG Отказ Вы можете попасть на потолок, когда вы пытаетесь отобразить несколько тысяч элементов одновременно. Ваш браузер может начать слопить под весом всех тех DOM элементы Отказ

Ну вот приходит HTML5 Canvas на помощь! Это намного быстрее, поэтому он может решить пылку вашего браузера.

Но вы можете быстро найти себя намущенным. Поскольку D3 и холст работает немного по-разному от D3 и SVG – особенно когда речь идет о рисунке и добавлении интерактивности.

Но не бойся – это не так сложно. Любой опыт, который вы имели со зданием визуалов с D3 и SVG – или приближаются к D3 с другим рендерером – помогут вам очень помогут.

Этот учебник построен на плечах гигантов, которые уже хорошо покрывали холст. Я узнал эти три учебника по сердцу, и я рекомендую вам тоже делать:

Так зачем продолжать читать это, то? Ну, когда я хочу узнать что-то новое, это помогает мне приятно посмотреть на ту же тему из немного разных углов. И это учебное пособие это немного другой угол.

Кроме того, этот учебник охватывает три ключевых этапа: Связывание данных , Рисование элементов и Добавление интерактивности – И это все это за один ход, с добавленной пошаговое руководство установить вас.

Что мы строим?

Сетка (много) квадратов. Их цвета не имеют никакого глубокого значения, но они не выглядят красиво? Важный бит состоит в том, что вы можете обновить его (чтобы охватить данные обвязки и обновления), что у него есть много элементов (до 10000, чтобы Canvas выплачиваться), и что вы можете наклоняться на каждом квадрате, чтобы показать информацию (интерактивность). Вы можете играть с этим здесь на полном экране или здесь со всем кодом

Ментальная модель

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

Первый шаг при использовании D3 обычно не связан с рисунком – он включает в себя подготовку всех ваших элементов, которые вы хотите рисовать. Это немного похоже на строительство немного Lego. Вы можете открыть коробку и начать строить что-то, или вы можете сначала посмотреть на руководство и построить его в соответствии с планом. Руководство – ваша ментальная модель, план или рецепт того, что вы хотите построить.

Что такое модель D3? Помимо большого количества полезных функций и методов, которые рассчитывают позиции, наборы данных повторной формы (макеты) и генерируют функции, которые рисуют, например, пути к нам, D3 имеет модель для того, как жизнь элементов должна развиваться на экране Отказ Он имеет определенный способ подумать о жизненном цикле каждого элемента.

Менее эфирно вы вводите данные в но несуществующую DOM, а D3 создает новые элементы вашего выбора в соответствии с приведенными вами данными. Обычно один элемент за счет данных. Если вы хотите ввести новые данные в DOM, вы можете сделать это, и D3 идентифицирует, какие элементы должны быть вновь созданы, какие элементы могут остаться, а какие элементы должны упаковать и оставить экран.

D3 обычно используется в сочетании с SVG или иногда с HTML-элементами. В этом православном случае вы можете увидеть данные в DOM, когда вы решите посмотреть на него через консоль. Вы можете схватить его, вы можете переместить его вверх или вниз по DOM, и вы можете – главное – добавьте интерактивность к каждому элементу, который вам нравится показать, например, всплывающую подсказку.

Но – на нижнем роде – вы не можете показать много элементов. Почему? Поскольку чем больше элементов, которые вы толкаете в DOM, тем сложнее браузер должен работать, чтобы отобразить их все. Пусть они также перемещаются, и браузер должен постоянно рассчитать их. Чем больше клыслей браузер получает более низкую частоту кадров или FPS (кадры в секунду), что измеряет, сколько кадров браузер может рисовать каждую секунду. Коэффициент кадров 60 – это хорошо и позволяет испытывать жидкий опыт до тех пор, пока никакие кадры не пропускаются – скорость кадров ничего до 30 лет может равняться бессмысленной езды. Поэтому, когда вы хотите показать больше элементов, вы можете вернуться к холсте.

Почему холст? Canvas – это элемент HTML5, который поставляется со своим собственным API для краски на нем. Все элементы, нарисованные на элементе холста, не проявляются в доме и сохраняют много работы для браузера. Они нарисованы в Немедленный режим Отказ Это означает, что сделанные элементы не будут сохранены в DOM, но ваши инструкции нарисуют их непосредственно к определенному кадру. Дом только знает один элемент холста; Все на нем только в памяти. Если вы хотите изменить свои элементы холста, вы должны перерисовать сцену для следующего кадра.

Проблема с этим, конечно, вы не можете напрямую общаться с этими нематериальными элементами, живущими в памяти. Вы должны найти способ поговорить с ними косвенно. Именно здесь модель D3 входит, а также пользовательские или «виртуальные» DOM-элементы. Что вы будете делать в главном, это:

  1. Свяжите ваши данные в пользовательских элементах DOM. Они не живут в доме, а только в памяти (в «виртуальном» DOM) и описывают жизненный цикл этих элементов известным образом D3.
  2. Используйте холст, чтобы нарисовать эти элементы.
  3. Добавьте интерактивность с техникой под названием «Picking».

Давай сделаем это.

Данные

Прежде чем начать код, давайте создам некоторые данные. Допустим, вы хотите 5000 данных данных. Итак, давайте создадим массив с 5000 элементами, каждый из которых является объектом только одним значением свойств, несущих индекс элемента. Вот как вы создаете это с d3.range () Отказ d3.range () Это функция утилиты D3, которая создает массив на основе его аргумента:

var data = [];
d3.range(5000).forEach(function(el) {
  data.push({ value: el }); 
});

Вот как данные выглядят в консоли

Ошибка!

Контейнер холста и его инструменты

Элемент Canvas представляет собой HTML-элемент. Это концептуально очень похоже на любой сопутствующий элемент SVG-родитель, который, как правило, обычно добавляю к простому контейнеру Div, как в:

Итак, давайте добавим его в свой контейнер с D3, как в …

var width = 750, height = 400;
var canvas = d3.select('#container')  .append('canvas')  .attr('width', width)  .attr('height', height);
var context = canvas.node().getContext('2d');

Вам также необходимо добавить контекст, который является панелью Toolbox Canvas. Переменная контекста со времени на объекте, несущей все свойства и методы, щетки и цвета, которые вам нужно нарисовать на холсте. Без контекста элемент холста останется пустым и белым. Это все, что вам нужно настроить – холст и его инструменты …

HTML

…это просто. Главная HTML-структура вашего сайта будет:

Coloured grids

 
 
...takes numbers between 1 and 10k
 

Структура JavaScript

На верхнем уровне вам нужно только 2 функции:

databind(data) {
  // Bind data to custom elements.
}
draw() {
  // Draw the elements on the canvas.
}

Дополнительно прямо вперед.

Связывать элементы

Чтобы привязать данные к элементам, которые вы сначала создаете базовый элемент для всех ваших пользовательских элементов, которые вы будете производить и рисовать. Если вы хорошо знаете D3, подумайте об этом как о замене в элемент SVG:

var customBase = document.createElement('custom');
var custom = d3.select(customBase); // This is your SVG replacement and the parent of all other elements

Затем вы добавляете некоторые настройки для вашей сетки. Короче говоря, эти настройки позволяют нарисовать сетку квадратов. 100 квадратов Создайте «посылку», и после 10 посылок есть разрыв линии (или после 1000 квадратов). Вы можете настроить это для разных «рассылки» квадратов или разных линий. Или просто не беспокоиться об этом. Я предлагаю последнее …

// Settings for a grid with 10 cells in a row, // 100 cells in a block and 1000 cells in a row.
var groupSpacing = 4; var cellSpacing = 2; var offsetTop = height / 5; var cellSize = Math.floor((width - 11 * groupSpacing) / 100) - cellSpacing;

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

function databind(data) {
// Get a scale for the colours - not essential but nice.
colourScale = d3.scaleSequential(d3.interpolateSpectral)                      .domain(d3.extent(data, function(d) { return d; }));

Теперь давайте присоединим к вашим данным к «замену-SVG», которое вы назвали Пользовательские Выше добавьте еще не существующие пользовательские элементы с классом .rect.rect.

var join = custom.selectAll('custom.rect')  .data(data);

Вы входите в пользовательские элементы (помните, что ничего не входит в DOM, это все в памяти).

var enterSel = join.enter()  .append('custom')  .attr('class', 'rect')  .attr("x", function(d, i) {    var x0 = Math.floor(i / 100) % 10, x1 = Math.floor(i % 10);         return groupSpacing * x0 + (cellSpacing + cellSize) * (x1 + x0 * 10); })  .attr("y", function(d, i) {  var y0 = Math.floor(i / 1000), y1 = Math.floor(i % 100 / 10);   return groupSpacing * y0 + (cellSpacing + cellSize) * (y1 + y0 * 10); })  .attr('width', 0)  .attr('height', 0);

Когда элемент входит в вашу модель, вы просто даете его положение x и y, а также ширину и высоту 0, что вы измените в предстоящем выборе обновления …

Вы объединяете выбор ввода в выделение обновления и определите все атрибуты для выбора обновления и ввода. Это включает в себя ширину и значение высоты, а также цвет из цветной массы, который вы построили ранее:

join   .merge(enterSel)  .transition()  .attr('width', cellSize)  .attr('height', cellSize)  .attr('fillStyle', function(d) { return colourScale(d); });

Две вещи заметок об этой последней линии. Когда вы работаете с SVG, эта линия будет

.style('color', function(d) { return colourScale(d); })

Но с холстом вы используете .attr () Отказ Почему? Ваш главный интерес здесь состоит в том, чтобы найти безболезненного способа передачи некоторой элементной информации. Здесь вы хотите перенести цветную строку из databind () к Рисовать () функция. Вы используете элемент просто как судно для транспортировки ваших данных в то, где он предоставляется на холсте.

Это очень важное различие: при работе с SVG или HTML вы можете связать данные на элементы и нарисовать или применить стили к элементам на одном шаге. В холсте вам нужно два шага. Сначала вы связываете данные, затем вы рисуете данные. Вы не можете стирать элементы во время привязки. Они существуют только в памяти, а холст не может быть введен в стиле CSS стиль, что именно то, что вы получаете при использовании .style () Отказ

Сначала это может показаться ограничением, поскольку вы можете сделать меньше на один шаг, но это концептуально почти чище, а также дает вам некоторую свободу. .attr () Позволяет нам отправлять любые пары ключа-значений в путешествие. Вы можете использовать другие методы, такие как HTML .dataset Свойство например, но .attr () будет просто хорошо.

Обратите внимание, что мы не говорим цвет Но FillStyle Отказ Если честно, вы могли бы использовать цвет или вы могли бы использовать Choochootrain здесь. Вам нужно было бы только вспомнить это, когда вы получите информацию позже во время нанесения. Однако, как Canvas использует свойство под названием FillStyle в элементы стиля, в этом случае кажется более подходящим.

Наконец, вы также определяете выбор выхода, решив, что должно произойти с существующими элементами.

var exitSel = join.exit()  .transition()  .attr('width', 0)  .attr('height', 0)  .remove();

Это оно! Вы можете закрыть ваш databind () Функция и двигаться дальше …

} // databind()

Это на самом деле не страшно, исходя из D3, так как оно почти точно так же. Теперь вы успешно создали свою модель данных, способ приложения подумает о данных. Каждый элемент получает свойства, которые необходимо нарисовать через .attr () Функции и каждый элемент будет назначен состояние жизненного цикла в зависимости от инъекционных данных. Наша стандартная модель D3.

Рисование элементов

Теперь вам нужно написать функцию Draw, чтобы получить элементы на экране. Давайте просто отметим здесь, что ничего не произошло. Вы не звонили databind () Тем не менее, потому что вам нужно найти способ нарисовать его на холст. Так что здесь мы идем … Рисовать () Функция не должна принимать какие-либо аргументы в этом случае:

function draw() {

Как упоминалось с мимолетно выше, вам нужно позаботиться о том, чтобы убирать холст каждый раз, когда вы нарисуете заново. Дом является материалом, в том случае, если вы нарисуете прямо-элемент на нем, и вы измените его значение x, он будет двигаться в направлении x, и DOM автоматически позаботится об этом движении (или повторной краске).

Если вы перемещаете прямо на определенный момент времени (после нажима на кнопку), например, браузер переместит прямо от 0 до 1 в одной тике или рамке (которая длиной до 16 мс). Если вы переместите его от 0 до 10, он будет делать это за то время, в зависимости от продолжительности вы задали этот переход, возможно, 1 пиксель на галочку, может быть, 8 пикселей на галочку (для более читают это Post).

Но он скажет пиксель при 0, что прямо исчез и пиксель при 1, что сейчас есть прямо. Холст не делает этого. Вы должны сказать холст, что покрасить, и если вы нарисуете что-то новое, вам нужно сказать ему, чтобы удалить предыдущую краску.

Итак, давайте начнем с уборки всего, что может быть на холсте, прежде чем рисовать. Вот как:

context.clearRect(0, 0, width, height); // Clear the canvas.

Простой.

Теперь ваша очередь…

  1. … придерживаться всех элементов, чтобы
  2. петля через все элементы и
  3. Возьмите информацию, которую вы сохранили в databind () Функция, чтобы нарисовать элемент:
// Draw each individual custom element with their properties.
var elements = custom.selectAll('custom.rect');// Grab all elements you bound data to in the databind() function.
elements.each(function(d,i) { // For each virtual/custom element...
  var node = d3.select(this);   // This is each individual element in the loop.     context.fillStyle = node.attr('fillStyle');   // Here you retrieve the colour from the individual in-memory node and set the fillStyle for the canvas paint
  context.fillRect(node.attr('x'), node.attr('y'), node.attr('width'), node.attr('height'));  // Here you retrieve the position of the node and apply it to the fillRect context function which will fill and paint the square.
}); // Loop through each element.

Вот и все! Вы можете закрыть Рисовать () функция

} // draw()

Когда я начал с холста через некоторое время желание погрузиться в него, эта простота действительно закручила духи.

Однако ничего не произошло в браузере. У нас есть инструменты в databind () и Рисовать () Функция, но ничего не было нарисовано. Как ты делаешь это? Если вы просто хотели нарисовать статический визуальный или изображение, вы просто звоните:

databind(data);
draw();

Это связывает данные на пользовательские элементы, которые будут жить в памяти, а затем нарисовать его – один раз!

Но у вас есть переходы. Помните выше: когда вы написали databind () Функция вы пересели ширину и высоту ячейки от 0 до их размера, а также цвет от черного (по умолчанию) к соответствующему цвету элемента. Переход D3 по умолчанию длится 250 миллисекунд, поэтому вам нужно много раз перерезать квадраты в этих 250 мс, чтобы получить плавный переход. Как ты делаешь это?

Это опять просто. Вы просто звоните ДАН DATABIND (данные) Чтобы создать наши пользовательские элементы, прежде чем вы повторно звоните Рисовать () до тех пор, пока требуется переход к запуску. Так что в нашем случае не менее 250 мс. Вы могли бы использовать SetInterval () Для этого, но мы действительно должны использовать ProwelsimationFrame () Для того, чтобы быть максимально как можно исполнять (для большего количества Читать это ). Есть несколько способов использовать его, но сохраняя в духе D3, я предлагаю использовать d3.timer () который реализует ProwelsimationFrame () а также прямой на использование. Так что здесь мы идем:

// === First call === //
databind(d3.range(value)); // Build the custom elements in memory.
var t = d3.timer(function(elapsed) {
  draw();
  if (elapsed > 300) t.stop();
}); // Timer running the draw function repeatedly for 300 ms.

d3.timer () Вызывает обратный вызов несколько раз, пока истекший (который является прошедшим временем в миллисекундах от мнения) составляет длиной 300, а затем таймер остановлен. В этих 300 миллисекунд он работает Рисовать () на каждом тике (примерно каждый 16 мс). Рисовать () Затем смотрится на атрибуты каждого элемента и опираются их соответственно.

Вот как работает переход в холсте. Вы называете функцию рисования сразу после функции связывания много раз. Какой бы ваша модель D3-модели настроен для перехода (позиции, цвета, размеры), будет повторно нарисована много раз с небольшими дополнительными изменениями для каждого розыгрыша

Обратите внимание, что Рисовать () нужно прийти сразу после databind () функция. Вы не могли задать машину работать databind () Затем сделайте что-нибудь еще на секунду, а затем позвоните Рисовать () Отказ Потому что через 1 секунду переходные состояния рассчитываются вашим databind () Функция уже переходила. Сделано, посыпается и забыта.

Это оно! Вы связанные данные на пользовательские элементы, и вы нарисовали его на холсте.

Пусть пользователь обновит количество квадратов

Чтобы дать пользователю возможность повторить этот подвиг с пользовательским номером элементов (OK, полуамонтаж максимум 10000), вы добавляете следующий прослушиватель и обработчик в окно текстового ввода:

// === Listeners/handlers === //
d3.select('#text-input').on('keydown', function() {
if (d3.event.keyCode === 13) { // Only do something if the user hits return (keycode 13).
  if (+this.value < 1 || +this.value > 10000) {   // If the user goes lower than 1 or higher than 10k...         d3.select('#text-explain').classed('alert', true);     // ... highlight the note about the range and return.
    return;
  } else {   // If the user types in a sensible number...
    d3.select('#text-explain').classed('alert', false);     // ...remove potential alert colours from the note...
    value = +this.value; // ...set the value...
    databind(d3.range(value)); // ...and bind the data.
    var t = d3.timer(function(elapsed) {
      draw();        if (elapsed > 300) t.stop();
    }); // Timer running the draw function repeatedly for 300 ms.     } // If user hits return.
}); // Text input listener/handler

Здесь опять же, наша красочная сетка квадратов холста, готовых к обновлению и перерисовке:

Интерактивность

Самая большая «боль» с холстом по сравнению с SVG или HTML в том, что в доме нет материальных элементов, живущих в доме. Если бы вы могли просто зарегистрировать слушателей к элементам и добавлять обработчики к слушателям. Например, вы можете вызвать мыши на SVG rect Элемент и всякий раз, когда слушатель триггеры, вы могли бы сделать что-то до прямого. Как показывать значения данных, хранящиеся с rect в подсказке.

С холстом вы должны найти другой способ сделать событие слышно на наших элементах холста. К счастью, есть ряд умных людей, которые думали о косвенном, но логичном пути.

Так какая интерактивность мы хотим? Как сказано выше, давайте пойдем на всплывающую подсказку и давайте предположим, что вы хотите показать индекс квадрата в подсказке, как только вы наведите курсор на элемент. Не очень захватываю, но ключ заключается в том, что вы можете получить доступ к данным, связанным с элементом, находящийся над ним.

Выбор

Есть несколько шагов (все логические, хотя). Но короче вы построите два холстаза для достижения этого. Один Главный холст это производит наш визуальный и один скрытый холст (Как и мы не видим), что производит тот же визуальный. Ключ вот в том, что все элементы на втором холсте будут в точном же позиции по отношению к значению Canvas по сравнению с первым холстом. Так что площадь 1 начинается на 0,0 на главном холсте, а также на скрытом холсте. Квадрат 2 начинается на 8,0 на главном холсте, а также на скрытом холсте и так далее.

Есть только одно важное различие. Каждый элемент на скрытом холсте получит уникальный цвет. Мы создадим объект (или скорее ассоциативный массив или карта для краткости), которая связывает каждый уникальный цвет для данных каждого элемента.

Почему? Поскольку затем мы прикрепим прослушиватель Mouse Move к главному холму, чтобы получить поток мышиных позиций. На каждом положении мыши мы можем использовать собственный способ для «выбрать» цвет в этой точной позиции. Тогда мы просто смотрим на цвет в нашем ассоциативном массиве, и у нас есть данные! И мы летим …

Вы могли бы сказать «Ну, мои квадраты уже есть уникальный цвет, я могу использовать те?» И действительно, вы могли бы использовать их. Тем не менее, ваша взаимодействие выйдет из окна, как только вы решите лишить свои квадраты из цветов. Таким образом, вы должны убедиться, что он всегда есть один холст – скрытый холст – у него есть гарантированный набор уникальных цветов для квадратов.

Давайте применим эту технику шаг за шагом. Код, который вы построили до сих пор, могут остаться – вы просто добавите к нему, когда вы идете.

1. Подготовьте скрытый холст

Сначала давайте создадим скрытый холст, который будет гаванить нашу визуальное с уникальным цветом на квадрат.

1.1 Создайте скрытый элемент холста и установите его CSS на {дисплей: нет; } Отказ

// Rename the main canvas and add a 'mainCanvas' class to it.
var mainCanvas = d3.select('#container')  .append('canvas')  .classed('mainCanvas', true)  .attr('width', width) .attr('height', height); // new ----------------------------------- 
// Add the hidden canvas and give it the 'hiddenCanvas' class.
var hiddenCanvas = d3.select('#container')  .append('canvas')  .classed('hiddenCanvas', true)   .attr('width', width)   .attr('height', height);

На самом деле, я не буду ставить холст, чтобы скрыть в этом примере, чтобы показать, что происходит. Но чтобы сделать это, просто добавьте .hiddencanvas {display: нет; } к вашим CSS и дел делается.

1.2 Создайте переменную контекста в Рисовать () Функция и пройти два аргумента функции: холст, а также логический, называемый «скрытым», определяющим, какой холст мы строительствуем

function draw(canvas, hidden) {

1.3 Теперь вам нужно адаптировать все функции рисования, чтобы включить два новых Рисовать () аргументы. Так что отныне, вы не просто звоните Рисовать () Вы звоните либо Рисовать (Maincanvas, false) или Рисовать (hiddencanvas, true)

2. Нанесите уникальные цвета на скрытые элементы и сопоставьте их

Здесь дорогой читатель, приходит ключевую часть нашей операции, двигатель нашего грузовика, специи в нашем супе.

2.1 Включите функцию для создания нового уникального цвета каждый раз, когда он вызывается (через Переполнение стека )

// Function to create new colours for the picking.
var nextCol = 1;
function genColor(){     var ret = [];
  if(nextCol < 16777215){         ret.push(nextCol & 0xff); // R     ret.push((nextCol & 0xff00) >> 8); // G     ret.push((nextCol & 0xff0000) >;> 16); // B
    nextCol += 1;     }
var col = "rgb(" + ret.join(',') + ")";
return col;
}

gencolour () производит цветную структуру в форме RGB (0,0,0). Каждый раз, когда он называется, это увеличивает значение R одно. После того, как он достигает 255, он увеличивает значение G на 1 и сбрасывает значение R до 0. После того, как он достигнет R (255,255,0), он увеличивает значение B на 1 сброс R и G до 0 и так далее.

Таким образом, в целом вы можете иметь 577.216 элементы, чтобы сохранить уникальный цвет. Тем не менее, я могу заверить вас, ваш браузер умрет заранее. Даже с холстом (учебник WebGL для последующей деятельности).

2.2 Создайте объект MAP, который будет отслеживать, какой пользовательский элемент имеет уникальный цвет:

var colourToNode = {}; // Map to track the colour of nodes.

Вы можете добавить gencolour () Функция везде, куда вы хотите в своем скрипте, до тех пор, пока он снаружи databind () и Рисовать () Сфера действия функции. Но обратите внимание, что ваша переменная карты должна быть создана до и за пределами объема databind () функция.

2.3 Добавьте уникальный цвет на каждый пользовательский элемент, например .attr ('fillstylehidden') и 2.4 Создайте карту-объект во время создания элемента

Здесь вы будете использовать свой «Цвет-канон» gencolour () в нашем databind () Функция при назначении FillStyle к нашим элементам. Поскольку у вас также есть доступ к каждому DataPoint, пока он связан с каждым элементом, вы можете объединить цвет и данные в вашем Colourtonode карта.

join   .merge(enterSel)   .transition()   .attr('width', cellSize)   .attr('height', cellSize)   .attr('fillStyle', function(d) {     return colorScale(d.value);   });
  // new -----------------------------------------------------         .attr('fillStyleHidden', function(d) { 
    if (!d.hiddenCol) {
      d.hiddenCol = genColor();       colourToNode[d.hiddenCol] = d;
    }
    // Here you (1) add a unique colour as property to each element     // and(2) map the colour to the node in the colourToNode-map.
    return d.hiddenCol;
});

2.5 Теперь вы можете раскрасить элементы в соответствии с холстом Рисовать () Функция рендеринга. Вы добавляете условный на FillStyle В Рисовать () Функция, применяя цвета для нашего визуального на главную холст и уникальные цвета к скрытому холсте. Это простой одноклассник:

context.fillStyle = hidden ? node.attr('fillStyleHidden') : node.attr('fillStyle');
// The node colour depends on the canvas you draw.

Главный холст все еще выглядит одинаково, конечно:

Давайте, наконец, добавим некоторую интерактивность и начните с рисования скрытого холста, когда мы перемещаем мышь на наш главный холст.

3. Подберите цвета с помощью мыши

3.1 Сначала просто зарегистрируйте слушателя на главный холст, прослушивая события перемещения мыши.

d3.select('.mainCanvas').on('mousemove', function() {
});

Почему MouseMove? Поскольку вы не можете зарегистрировать слушателей с индивидуальными квадратами, но должны использовать весь холст, который вы не сможете работать с событиями Mouseover или-NOUT, так как они будут только вызвать при входе в холсте, а не элементы. Чтобы получить положение мыши на холсте, вы можете сделать MouseMove или щелкнуть/Mousedown.

d3.select('.mainCanvas').on('mousemove', function() {
  draw(hiddenCanvas, true); // Draw the hidden canvas.
});

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

Цвета на главном холсте варьируются от черного до красного, от RGB (0,0,0) до RGB (255,0,0), а затем он выглядит так, как будто такой же диапазон от черного до красного, повторяется. Однако теперь цветные варьируются от чуть более зеленее черного, именно от RGB (0,1,0) до RGB (255,1,0):

Увеличиваясь в первые пару сотен квадратов, вот цвета первой, 256-й и 257-й площади:

3.3 Поскольку наш скрытый холст структурно является структурным копией нашего главного холста, все скрытые элементы холста будут в том же положении, что и элементы на нашем главном холсте. Итак, теперь вы можете использовать позиции Mouse’s X и Y, вы собираете с слушателя на главном холсте, чтобы установить то же место на скрытом холсте. Вернуться в слушатель, вы добавляете:

d3.select('.mainCanvas').on('mousemove', function() {       // Draw the hidden canvas.  draw(hiddenCanvas, true);
  // Get mouse positions from the main canvas.  var mouseX = d3.event.layerX || d3.event.offsetX;   var mouseY = d3.event.layerY || d3.event.offsetY; });

Обратите внимание, что здесь мы берем event.layerx и Event.layery Свойства, которые возвращают положение мыши, включая прокрутку. Это может сломаться Так что используйте Offsetx в качестве отблески (или просто использовать OffsetX).

3.4 Выбор: холст значительно позволяет доступом к пикселям-данным мышь зависает с помощью Getimagedata () функция и ее .дата имущество. В полном расцвете это будет выглядеть так:

getimagedata (posx, posy, 1, 1) .data Отказ

Он вернет массив с четырьмя числами: R, G, G, B и Alpha Value. Как вы усердно построили Colourtonode Карта, присваивая данные элемента каждому из своих скрытых цветов, теперь вы можете получить доступ к данным этого элемента, просто просматривая цвет на карте!

d3.select('.mainCanvas').on('mousemove', function() {
  // Draw the hidden canvas.  draw(hiddenCanvas, true);
  // Get mouse positions from the main canvas.  var mouseX = d3.event.layerX || d3.event.offsetX;   var mouseY = d3.event.layerY || d3.event.offsetY;
// new -----------------------------------------------
  // Get the toolbox for the hidden canvas.  var hiddenCtx = hiddenCanvas.node().getContext('2d');
  // Pick the colour from the mouse position.   var col = hiddenCtx.getImageData(mouseX, mouseY, 1, 1).data; 
  // Then stringify the values in a way our map-object can read it.  var colKey = 'rgb(' + col[0] + ',' + col[1] + ',' + col[2] + ')';
  // Get the data from our map!   var nodeData = colourToNode[colKey];
  console.log(nodeData);
});

И действительно, регистрация Nodedata К консоли возвращает объект каждый раз, когда вы паритете на квадрат:

Данные на узел теперь показывают ценность который представляет собой оригинальные данные, а также ключевую Hiddencol Показаны цвета этого узла для скрытого холста:

3.5 Наконец – и это формальность – вы добавляете всплывающую подсказку

d3.select('.mainCanvas').on('mousemove', function() {
  // Draw the hidden canvas.  draw(hiddenCanvas, true);
  // Get mouse positions from the main canvas.  var mouseX = d3.event.layerX || d3.event.offsetX;   var mouseY = d3.event.layerY || d3.event.offsetY;
  // Get the toolbox for the hidden canvas.  var hiddenCtx = hiddenCanvas.node().getContext('2d');
  // Pick the colour from the mouse position.   var col = hiddenCtx.getImageData(mouseX, mouseY, 1, 1).data;
  // Then stringify the values in a way our map-object can read it.  var colKey = 'rgb(' + col[0] + ',' + col[1] + ',' + col[2] + ')';
  // Get the data from our map!   var nodeData = colourToNode[colKey];     console.log(nodeData);
  // new -----------------------------------------------
  if (nodeData) {   // Show the tooltip only when there is nodeData found by the mouse
    d3.select('#tooltip')       .style('opacity', 0.8)       .style('top', d3.event.pageY + 5 + 'px')       .style('left', d3.event.pageX + 5 + 'px')         .html(nodeData.value); 
  } else {   // Hide the tooltip when the mouse doesn't find nodeData.      d3.select('#tooltip').style('opacity', 0);     }
}); // canvas listener/handler

Это оно! Вы визуализировали большое количество элементов на холсте – больше, чем вы смогли наслаждаться проблемным с SVG. Вы все еще использовали модель жизненного цикла D3, и вы добавили некоторую интерактивность для доступа к данным, прикрепленным к каждому элементу. Эти три шага должны позволить вам сделать в значительной степени все или, по крайней мере, больше, чем то, к которому вы привыкли при работе с D3 и SVG.

Есть пошаговое руководство С нуля до интерактивного D3/холста в моем блоге, который позволяет внутренние ссылки страницы. Таким образом, вы можете увидеть весь процесс в одном представлении и щелкнуть свой путь через него с легкостью:

… и Вот полный код снова Отказ

Я надеюсь, тебе понравилось читать это, и, пожалуйста, скажем, Привет и/или …

Ларс Верпухл www.datamake.io @lars_vers. https://www.linkedin.com/in/larsverspohl

… всегда благодарен за собой? Или следующее он может вернуться.