Рисование с использованием SVG-элементов


Мы знакомы с основной структурой SVG-изображения и его элементов. Как нам теперь начать генерировать фигуры из наших данных?

Вы, наверное, заметили, что все свойства SVG-элементов задаются в виде атрибутов. Это значит, что они включены внутрь тега элемента в виде ключ/значение, например:

<element property="value"/>

Хммм, уж больно сильно напоминает HTML:

<p class="eureka"/>

Мы уже использовали удобные методы append() и attr() из библиотеки D3 для создания новых HTML элементов и установки их атрибутов. Поскольку SVG-элементы являются частью DOM, так же как и HTML-элементы, мы можем использовать append() и attr() точно так же для создания SVG-изображений!


Создаем SVG

Первоначально, нам надо создать SVG-элемент, в который мы будем включать все наши фигуры.

d3.select("body").append("svg");

Этим кодом мы ищем элемент body и добавляем в него элемент SVG перед закрывающимся тегом </body>. Я рекомендую сделать так:

var svg = d3.select("body").append("svg");

Помните, сколько методов D3 возвращают ссылку на DOM-элементы, с которыми они работают? Путем создания переменной svg, мы можем запомнить ссылку, возвращенную методом append(). Считайте svg не просто "переменной", а "указателем на SVG-объект, который мы только что создали". Эта ссылка сохранит нам много кода далее. Вместо того, чтобы искать каждый раз элемент SVG кодом d3.select("svg"), мы просто используем ссылку svg.

svg.attr("width", 500)
	.attr("height", 50);

Иначе, все могло бы быть записано в одной строке кода:

var svg = d3.select("body")
            .append("svg")
            .attr("width", 500)
            .attr("height", 50);

Посмотрите демо-страницу с этим кодом. Проинспектируйте DOM, и убедитесь, что пустой SVG-элемент в нее включен.

Чтобы упростить вам жизнь, я рекомендую поместить в переменные значения width и height в начало вашего кода, как здесь.

//Width and height
var w = 500;
var h = 50;

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

var svg = d3.select("body")
            .append("svg")
            .attr("width", w)   // <-- Here
            .attr("height", h); // <-- and here!

Фигуры на основе данных

Пришло время добавить несколько фигур. Я буду использовать наш первоначальный набор данных:

var dataset = [ 5, 10, 15, 20, 25 ];

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

svg.selectAll("circle")
    .data(dataset)
    .enter()
    .append("circle");

Запомните, что selectAll() вернет пустые ссылки на все круги(которые пока не существуют), data() привязывает значения к элементам, которые мы хотим создать, enter() возвращает ссылки на шаблоны новых элементов, а метод append() в конце-концов добавляет круги в DOM.

Чтобы в последующем нам было проще ссылаться на созданные круги, мы создадим переменную, и в ней будем хранить ссылки на новые элементы:

var circles = svg.selectAll("circle")
                 .data(dataset)
                 .enter()
                 .append("circle");

Великолепно, но всем этим кругам надо задать позицию и размер. Будьте осторожны, следующий код может взорвать ваш мозг:

circles.attr("cx", function(d, i) {
            return (i * 50) + 25;
        })
       .attr("cy", h/2)
       .attr("r", function(d) {
            return d;
       });
Результат работы кода

Насладитесь результатом на демо-странице. Давайте по шагам разберем код:

circles.attr("cx", function(d, i) {
            return (i * 50) + 25;
        })

Берем ссылку на все круги и устанавливаем атрибут cx каждому. Наши значения уже привязаны к элементам кругов, поэтому для каждого круга значение d соответствует необходимому значению в нашем первоначальном наборе данных(5, 10, 15, 20 и 25). Второе значение, i, также автоматически заполняется для нас. i хранит номер позиции(в наборе данных) текущего значения, соответствующего данному элементу. Номера позиций считаются с нуля, поэтому для нашего первого круга i == 0, для второго i == 1 и так далее. Мы используем i, чтобы вставлять круги справа, потому что в каждом последующем цикле значение i увеличивается.

(0 * 50) + 25 returns 25
(1 * 50) + 25 returns 75
(2 * 50) + 25 returns 125
(3 * 50) + 25 returns 175
(4 * 50) + 25 returns 225

Чтобы убедиться в том, что переменная i доступна внутри вашей функции, вы должны включить переменную в определение функции в качестве аргумента(function(d, i)). Также вы должны включить d, даже если вы не используете d в вашей функции(как в случае выше).

На следующей строке:

.attr("cy", h/2)

h - это высота всего SVG-элемент, поэтому h/2 - это половина его высоты. Это сделано для того, чтобы круги были расположены в центре SVG-элемента по вертикали.

.attr("r", function(d) {
    return d;
});

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


Приятные цвета

Цветовая закраска и рамка это всего лишь другие атрибуты, которые вы можете установить, используя те же методы. Просто, добавив этот код:

.attr("fill", "yellow")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {
    return d/2;
});

мы получим следующий результат:

Результат работы кода

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

Автором оригинального текста книги D3 Tutorials является Scott Murray
На русский язык перевел Ivanov Sergey. 2014 год