Различия, перечисленные в связанном вопросе / ответах, говорят об общих различиях между svg и canvas (вектор / растр и т. Д.). Однако в случае d3 эти различия имеют дополнительные последствия, особенно если учесть, что основной частью d3 является привязка данных.
Привязка данных
Возможно, наиболее важной особенностью d3 является привязка данных. Майк Босток утверждает, что ему нужно было создать d3 после объединения данных с элементами:
Определяющим моментом было то, когда я получил объединение данных, работающее для первого
время. Это было волшебство. Я даже не был уверен, что понял, как это работает, но
это был взрыв, чтобы использовать. Я понял, что может быть практический инструмент для
визуализация, которая не излишне ограничивает типы
визуализации, которые вы могли бы сделать. ссылка
В SVG привязка данных проста - мы можем назначить данные для отдельного элемента svg, а затем использовать эти данные для установки его атрибутов / обновления их / и т. Д. Это основано на состоянии svg - мы можем повторно выбрать круг и изменить его или получить доступ к его свойствам.
В Canvas холст не имеет состояния, поэтому мы не можем привязать данные к фигурам в пределах холста, поскольку холст состоит только из пикселей. Таким образом, мы не можем выбирать и обновлять элементы внутри холста, потому что холст не имеет элементов для выбора.
Исходя из вышеизложенного, мы можем видеть, что цикл ввода / обновления / выхода (или базовые операторы добавления) необходимы для svg в идиоматическом D3: нам нужно вводить элементы, чтобы их видеть, и мы часто их стилизуем на основе их данных , В случае с canvas нам не нужно вводить 1018 *, как и при выходе / обновлении. Нет элементов для добавления, чтобы видеть, поэтому мы можем рисовать визуализации без подходов ввода / обновления / выхода или добавления / вставки, используемых в визуализациях d3 svg, , если мы хотим .
Холст без привязки данных
Я буду использовать пример bl.ock в вашем последнем вопросе, здесь . Поскольку нам вообще не нужно добавлять элементы (или добавлять к ним данные), мы используем цикл forEach для рисования каждой функции (что противоречит идиоматическому D3 с SVG). Поскольку нет элементов для обновления, мы должны перерисовывать каждый объект каждый тик - перерисовывать весь кадр (обратите внимание на очистку холста каждый тик). Что касается перетаскивания, d3.drag и d3.force имеют некоторую функциональность, предполагающую использование с canvas, и могут позволить нам изменять массив данных напрямую с помощью событий перетаскивания, минуя любую потребность в элементах узла в DOM для непосредственного взаимодействия с мышью (d3 .force также напрямую изменяет массив данных - но делает это и в примере svg ).
Без привязки данных мы рисуем элементы на основе данных напрямую:
data.forEach(function(d) {
// drawing instructions:
context.beginPath()....
})
Если данные изменятся, мы, вероятно, перерисоваем данные.
Холст с привязкой данных
Тем не менее, вы можете реализовать связывание данных с canvas, но для этого требуется другой подход с использованием фиктивных элементов. Мы проходим обычный цикл обновления / выхода / ввода, но, поскольку мы используем фиктивные элементы, ничего не отображается. Мы перерисовываем холст всякий раз, когда хотим (это может быть непрерывно, если мы используем переходы), и рисуем вещи на основе фиктивных элементов.
Для создания фиктивного родительского контейнера мы можем использовать:
// container for dummy elements:
var faux = d3.select(document.createElement("custom"));
Затем мы можем делать выборки по мере необходимости, используя ввод / выход / обновление / добавление / удаление / переход / etc:
// treat as any other DOM elements:
var bars = faux.selectAll(".bar").data(data).enter()....
Но так как элементы в этих выборках не отображаются, нам нужно указать, как и когда их рисовать. Без привязки данных и Canvas мы рисовали элементы на основе данных напрямую, с привязкой данных и Canvas мы рисовали на основе выделения / элемента в поддельном DOM:
bars.each(function() {
var selection = d3.select(this);
context.beginPath();
context.fillRect(selection.attr("x"), selection.attr("y")...
...
})
Здесь мы можем перерисовывать элементы всякий раз, когда мы выходим / вводим / обновляем и т. Д., Что может иметь некоторые преимущества. Это также позволяет осуществлять переходы D3 путем непрерывного перерисовывания при переходе свойств к искусственным элементам.
В следующем примере представлен полный цикл ввода / вывода / обновления с переходами, демонстрирующий холст с привязкой данных:
var canvas = d3.select("body")
.append("canvas")
.attr("width", 600)
.attr("height", 200);
var context = canvas.node().getContext("2d");
var data = [1,2,3,4,5];
// container for dummy elements:
var faux = d3.select(document.createElement("custom"));
// normal update exit selection with dummy elements:
function update() {
// modify data:
manipulateData();
var selection = faux.selectAll("circle")
.data(data, function(d) { return d;});
var exiting = selection.exit().size();
var exit = selection.exit()
.transition()
.attr("r",0)
.attr("cy", 70)
.attr("fill","white")
.duration(1200)
.remove();
var enter = selection.enter()
.append("circle")
.attr("cx", function(d,i) {
return (i + exiting) * 20 + 20;
})
.attr("cy", 50)
.attr("r", 0)
.attr("fill",function(d) { return ["orange","steelblue","crimson","violet","yellow"][d%5]; });
enter.transition()
.attr("r", 8)
.attr("cx", function(d,i) {
return i * 20 + 20;
})
.duration(1200);
selection
.transition()
.attr("cx", function(d,i) {
return i * 20 + 20;
})
.duration(1200);
}
// update every 1.3 seconds
setInterval(update,1300);
// rendering function, called repeatedly:
function render() {
context.clearRect(0, 0, 600, 200);
faux.selectAll("circle").each(function() {
var sel = d3.select(this);
context.beginPath();
context.arc(sel.attr("cx"),sel.attr("cy"),sel.attr("r"),0,2*Math.PI);
context.fillStyle = sel.attr("fill");
context.fill();
context.stroke();
})
window.requestAnimationFrame(render)
}
window.requestAnimationFrame(render)
// to manipulate data:
var index = 6; // to keep track of elements.
function manipulateData() {
data.forEach(function(d,i) {
var r = Math.random();
if (r < 0.5 && data.length > 1) {
data.splice(i,1);
}
else {
data.push(index++);
}
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Блочная версия .
Резюме
При использовании canvas для привязки данных требуется набор фиктивных элементов, но после привязки вы можете легко использовать переходы и цикл обновления / ввода / выхода. Но рендеринг отделен от обновления / ввода / выхода и переходов - вам решать, как и когда перерисовывать визуализацию. Этот рисунок происходит вне методов обновления / ввода / выхода и перехода.
С svg цикл ввода / обновления / выхода и переходы обновляют элементы в визуализации, связывая рендеринг и данные за один шаг.
В канве с привязкой данных к искусственным элементам визуализация представляет искусственные узлы. В SVG визуализация это узлы.
Связывание данных является принципиальным отличием, идиоматический D3 требует его в SVG, но дает нам выбор, хотим ли мы использовать его при работе с Canvas.
Однако существуют другие различия между Canvas и SVG по отношению к D3, упомянутые ниже:
Интерактивность
Возможно, наиболее существенной проблемой при использовании Canvas является то, что он не имеет состояния, это просто набор пикселей, а не элементов. Это затрудняет события мыши при взаимодействии с конкретными визуализированными фигурами. В то время как мышь может взаимодействовать с Canvas, стандартные события запускаются для взаимодействия с определенными пикселями.
Таким образом, в то время как в SVG мы можем назначить прослушиватель щелчков (например) для каждого узла в силовом макете, в Canvas мы устанавливаем прослушиватель щелчков для всего холста, а затем на основе позиции должны определить, какой узел следует рассматривать "щелкнул".
Упомянутый выше пример canvas D3-force использует метод .find
принудительной разметки и использует его для поиска узла, ближайшего к щелчку мыши, а затем устанавливает объект перетаскивания для этого узла.
Есть несколько способов определить, с какой визуализированной формой взаимодействует:
- Создание скрытого холста, предоставляющего справочную карту для отрисованных фигур
Каждая фигура в видимом холсте рисуется на невидимом холсте, но на невидимом холсте она имеет уникальный цвет. Взяв xy события мыши на видимом холсте, мы можем использовать его, чтобы получить цвет пикселя с тем же xy на невидимом холсте. Поскольку цвета - это числа в HTML, мы можем преобразовать этот цвет в индекс элемента данных.
Инвертирование шкал (масштабированное положение xy в немасштабированные входные значения) для карты температур / данных с сеткой ( пример )
Использование метода .find
неопределяемой диаграммы Вороного для нахождения ближайшего к событию узла (для точек, окружностей)
- Использование метода
.find
раскладки сил для поиска ближайшего узла к событию (для точек, окружностей, в основном в контексте раскладок сил)
- Использование прямой математики, квадродерев или других методов
Первый может быть самым распространенным и, безусловно, наиболее гибким, но другие могут быть предпочтительнее в зависимости от контекста.
Performance
Я очень быстро коснусь производительности. В связанном посте квеста " В чем разница между SVG и Canvas ", он может быть недостаточно смелым в ответах, но в целом canvas и svg различаются по времени рендеринга при обработке тысяч узлов, особенно если рендеринг тысячи узлов, которые анимируются.
Холст становится все более производительным по мере того, как рендерится больше узлов, а узлы делают больше вещей (переход, перемещение и т. Д.).
Вот краткое сравнение Canvas (с привязкой данных на поддельных узлах) и SVG и 19 200 одновременных переходов:
Холст должен быть более гладким из двух.
Модули D3
Наконец, я коснусь модулей D3. Большинство из них вообще не взаимодействуют с DOM и могут легко использоваться для SVG или Canvas. Например, d3-quadtree или d3-time-format не являются специфичными для SVG или Canvas, так как они вообще не имеют отношения к DOM или рендерингу. Модули, такие как d3-иерархия, на самом деле тоже ничего не рендерит, но предоставляют информацию, необходимую для рендеринга в Canvas или SVG.
Большинство модулей и методов, которые предоставляют данные о путях SVG, также можно использовать для генерации вызовов методов пути холста и, следовательно, относительно легко использовать для SVG и Canvas.
Я специально упомяну пару модулей:
D3-выбор
Очевидно, что этот модуль требует выборки, выборки требуют элементов. Поэтому, чтобы использовать это с Canvas для таких вещей, как цикл ввода / обновления / выхода или выбора .append / remove / lower / Повышение, мы хотим использовать искусственные элементы с Canvas.
С Canvas прослушиватели событий, назначенные с selection.on()
, могут работать с привязкой данных или без нее, проблемы взаимодействия с мышью отмечены выше.
D3-переход
Этот модуль переносит свойства элементов, поэтому он обычно используется с Canvas, только если мы использовали привязку данных к искусственным элементам.
D3 ось
Этот модуль является строго SVG, если только вы не готовы приложить немало усилий для его использования в Canvas. Этот модуль чрезвычайно полезен при работе с SVG, особенно при перемещении оси.
D3-путь
Это берет команды пути Canvas и преобразует их в данные пути SVG. Полезно для принятия кода холста в ситуациях SVG. В основном используется внутри D3 для получения данных о путях SVG.