Icicle-диаграмма представляет собой способ визуализации иерархических данных в виде вложенных прямоугольников, расположенных друг над другом или последовательно вдоль оси. Каждый уровень иерархии отображается как горизонтальная полоса, внутри которой размещаются дочерние элементы. Площадь или длина каждого прямоугольника пропорциональна значению узла, что позволяет быстро оценивать распределение данных в структуре.
Основой построения таких диаграмм в веб-разработке выступает D3.js, предоставляющая инструменты для работы с иерархическими данными, вычисления раскладок и управления SVG-элементами.
Icicle-диаграмма опирается на древовидную структуру данных, где каждый узел может содержать потомков:
{
"name": "root",
"value": 100,
"children": [
{
"name": "A",
"value": 60,
"children": [
{ "name": "A1", "value": 30 },
{ "name": "A2", "value": 30 }
]
},
{
"name": "B",
"value": 40
}
]
}
Важные характеристики структуры:
Для построения используется алгоритм partition layout, который преобразует дерево в координаты прямоугольников.
Основная идея:
ось X отвечает за ширину сегмента
ось Y отвечает за уровень глубины
каждый узел получает координаты:
x0, x1 — горизонтальные границыy0, y1 — вертикальные границыФормально распределение базируется на вычислении суммы значений потомков и пропорциональном делении пространства.
D3.js предоставляет модуль d3.hierarchy и
d3.partition, которые используются совместно.
const width = 900;
const height = 500;
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
const partition = d3.partition()
.size([width, height]);
partition(root);
svg.selectAll("rect")
.data(root.descendants())
.enter()
.append("rect")
.attr("x", d => d.x0)
.attr("y", d => d.y0)
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0)
.attr("fill", d => d.depth === 0 ? "#ccc" : "#69b3a2")
.attr("stroke", "#fff");
d3.hierarchy преобразует обычный JSON в структуру узлов
с дополнительными метаданными:
depth — глубина узлаheight — расстояние до листьевparent — ссылка на родителяchildren — массив потомковФункция .sum() задаёт правило вычисления значений
листьев, а .sort() определяет порядок отображения.
Partition-алгоритм делит пространство следующим образом:
Этот механизм обеспечивает строгую вложенность без пересечений.
Цвет используется для различения уровней или категорий:
const color = d3.scaleOrdinal()
.domain([0, 1, 2, 3])
.range(["#e0f3db", "#a8ddb5", "#43a2ca", "#0868ac"]);
svg.selectAll("rect")
.attr("fill", d => color(d.depth));
Варианты применения:
Добавление текстовых меток требует учета размеров прямоугольников:
svg.selectAll("text")
.data(root.descendants())
.enter()
.append("text")
.attr("x", d => d.x0 + 4)
.attr("y", d => d.y0 + 15)
.text(d => d.data.name)
.style("font-size", "12px")
.style("fill", "#000");
Ограничение:
d.x1 - d.x0Размеры icicle-диаграммы зависят от контейнера. Используются:
viewBox для масштабируемости SVGconst partition = d3.partition()
.size([width, height - 20]);
При изменении размеров требуется повторный вызов layout.
Поведение диаграммы расширяется через события:
rect.on("mouseover", function(event, d) {
d3.select(this).attr("opacity", 0.7);
});
rect.on("mouseover", function(event, d) {
svg.selectAll("rect")
.attr("opacity", node => node.ancestors().includes(d) ? 1 : 0.3);
});
const tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("background", "#fff");
rect.on("mousemove", (event, d) => {
tooltip
.style("left", event.pageX + 10 + "px")
.style("top", event.pageY + 10 + "px")
.html(`${d.data.name}: ${d.value}`);
});
При работе с глубокими деревьями возникают ограничения:
Оптимизации:
hierarchy.sum()Icicle-диаграмма часто сопоставляется с:
Особенность icicle:
CSS применяется для улучшения визуального восприятия:
rect {
shape-rendering: crispEdges;
}
text {
pointer-events: none;
}
Дополнительно:
strokerx, ry<defs>Для сложных систем применяется логика агрегации:
Каждый уровень может иметь собственную цветовую схему и правила отображения.
При динамическом обновлении данных:
hierarchypartitionconst newRoot = d3.hierarchy(newData).sum(d => d.value);
partition(newRoot);
svg.selectAll("rect")
.data(newRoot.descendants())
.join("rect")
.transition()
.attr("x", d => d.x0)
.attr("width", d => d.x1 - d.x0);
Icicle-диаграмма создаёт линейное ощущение иерархии, где:
Такое представление эффективно для анализа распределённых систем, структур каталогов, финансовых моделей и аналитических деревьев.