Claude D3.js Skill: мастер кастомных визуализаций
Забудьте про Chart.js и Highcharts. Если вам нужны действительно уникальные, интерактивные визуализации с полным контролем над каждым пикселем — вам нужен claude-d3js-skill от Chris von Csefalvay.
Что это за магия
D3.js (Data-Driven Documents) — это библиотека, которая связывает данные с DOM-элементами и применяет data-driven трансформации. Claude D3.js Skill превращает Claude Code в эксперта по созданию кастомных визуализаций любой сложности.
Что умеет:
- Chord diagrams, heatmaps, force-directed networks
- Интерактивные карты с кастомными проекциями
- Анимированные переходы и хореографированные трансформации
- Публикационно-качественная графика с контролем каждого элемента
- Интеграция с React, Vue, Svelte, ванильным JS
Установка и настройка
Скилл находится в /skills/claude-d3js-skill/
Быстрый старт:
# NPM
npm install d3
# CDN (для прототипов)
<script src="https://d3js.org/d3.v7.min.js"></script>
# ES6 import
import * as d3 from 'd3';Структура проекта:
claude-d3js-skill/
├── SKILL.md # Полная документация
├── references/
│ ├── d3-patterns.md # Библиотека паттернов
│ ├── scale-reference.md # Справочник по scales
│ └── colour-schemes.md # Цветовые палитры
└── assets/
├── chart-template.js # Базовый темплейт
├── interactive-template.js # С интерактивностью
└── sample-data.json # Тестовые данныеДва подхода к интеграции
Паттерн A: Direct DOM manipulation (рекомендуется)
D3 напрямую манипулирует DOM — классика для сложных визуализаций:
function drawChart(data) {
if (!data || data.length === 0) return;
const svg = d3.select('#chart');
svg.selectAll("*").remove(); // Очистка
const width = 800, height = 400;
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Scales, оси, визуализация...
}
// Вызов при изменении данных
drawChart(myData);Паттерн B: Declarative rendering (для фреймворков)
D3 только для вычислений, отрисовка через фреймворк:
function getChartElements(data) {
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, 400]);
return data.map((d, i) => ({
x: 50, y: i * 30,
width: xScale(d.value), height: 25
}));
}
// React: {getChartElements(data).map((d, i) =>
// <rect key={i} {...d} fill="steelblue" />)}
// Vue: v-for directive
// Svelte: {#each getChartElements(data) as d}Стандартная структура визуализации
Claude D3.js Skill учит структурировать код по стандартному паттерну:
function drawVisualization(data) {
if (!data || data.length === 0) return;
const svg = d3.select('#chart');
svg.selectAll("*").remove();
// 1. Define dimensions
const width = 800, height = 400;
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
// 2. Create main group with margins
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// 3. Create scales
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.x)])
.range([0, innerWidth]);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.y)])
.range([innerHeight, 0]); // Инвертировано для SVG
// 4. Create axes
g.append("g")
.attr("transform", `translate(0,${innerHeight})`)
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale));
// 5. Bind data and create elements
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => xScale(d.x))
.attr("cy", d => yScale(d.y))
.attr("r", 5)
.attr("fill", "steelblue");
}Этот паттерн универсален: margin convention, scales, axes, data binding — основа любой D3 визуализации.
Продвинутые паттерны
Chord Diagram — связи между сущностями:
function drawChordDiagram(data) {
// data: [{ source: 'A', target: 'B', value: 10 }, ...]
const nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])));
const matrix = Array(nodes.length).fill().map(() => Array(nodes.length).fill(0));
// Заполняем матрицу
data.forEach(d => {
const i = nodes.indexOf(d.source);
const j = nodes.indexOf(d.target);
matrix[i][j] += d.value;
matrix[j][i] += d.value;
});
// Chord layout
const chord = d3.chord().padAngle(0.05);
const arc = d3.arc().innerRadius(200).outerRadius(230);
const ribbon = d3.ribbon();
const chords = chord(matrix);
// Рисуем ribbons (связи)
g.selectAll("path")
.data(chords)
.join("path")
.attr("d", ribbon)
.attr("fill", d => colorScale(nodes[d.source.index]));
// Рисуем arcs (узлы)
g.selectAll("g")
.data(chords.groups)
.join("g")
.append("path")
.attr("d", arc)
.attr("fill", d => colorScale(nodes[d.index]));
}Heatmap — цветовое кодирование значений:
function drawHeatmap(data) {
// data: [{ row: 'A', column: 'X', value: 10 }, ...]
const rows = Array.from(new Set(data.map(d => d.row)));
const columns = Array.from(new Set(data.map(d => d.column)));
const xScale = d3.scaleBand().domain(columns).range([0, width]).padding(0.01);
const yScale = d3.scaleBand().domain(rows).range([0, height]).padding(0.01);
const colorScale = d3.scaleSequential(d3.interpolateYlOrRd)
.domain([0, d3.max(data, d => d.value)]);
g.selectAll("rect")
.data(data)
.join("rect")
.attr("x", d => xScale(d.column))
.attr("y", d => yScale(d.row))
.attr("width", xScale.bandwidth())
.attr("height", yScale.bandwidth())
.attr("fill", d => colorScale(d.value));
// Color legend
const legendScale = d3.scaleLinear()
.domain(colorScale.domain())
.range([legendHeight, 0]);
// Gradient в legend
for (let i = 0; i < legendHeight; i++) {
legend.append("rect")
.attr("y", i).attr("width", 20).attr("height", 1)
.attr("fill", colorScale(legendScale.invert(i)));
}
}Force-directed Network — живая сеть узлов:
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody().strength(-300))
.force("center", d3.forceCenter(width / 2, height / 2));
// Узлы с драг-дропом
const node = g.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", 8)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// Симуляция обновляет позиции
simulation.on("tick", () => {
link.attr("x1", d => d.source.x).attr("y1", d => d.source.y)
.attr("x2", d => d.target.x).attr("y2", d => d.target.y);
node.attr("cx", d => d.x).attr("cy", d => d.y);
});Интерактивность и анимации
Tooltips:
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden");
circles
.on("mouseover", (event, d) => {
tooltip.style("visibility", "visible")
.html(`<strong>${d.label}</strong><br/>Value: ${d.value}`);
})
.on("mousemove", (event) => {
tooltip.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px");
})
.on("mouseout", () => tooltip.style("visibility", "hidden"));Zoom & Pan:
const zoom = d3.zoom()
.scaleExtent([0.5, 10])
.on("zoom", (event) => {
g.attr("transform", event.transform);
});
svg.call(zoom);Smooth transitions:
// Базовая анимация
circles.transition().duration(750).attr("r", 10);
// Цепочка анимаций
circles.transition().duration(500).attr("fill", "orange")
.transition().duration(500).attr("r", 15);
// Staggered animation
circles.transition()
.delay((d, i) => i * 50)
.duration(500)
.ease(d3.easeBounceOut)
.attr("cy", d => yScale(d.value));Responsive дизайн
Скилл учит делать визуализации адаптивными:
function setupResponsiveChart(containerId, data) {
const container = document.getElementById(containerId);
const svg = d3.select(`#${containerId}`).append('svg');
function updateChart() {
const { width, height } = container.getBoundingClientRect();
svg.attr('width', width).attr('height', height);
drawChart(data, svg, width, height);
}
updateChart();
window.addEventListener('resize', updateChart);
return () => window.removeEventListener('resize', updateChart);
}
// Или через ResizeObserver (лучше)
const observer = new ResizeObserver(() => {
const { width, height } = svgElement.getBoundingClientRect();
redrawChart(width, height);
});
observer.observe(containerElement);Scales Reference
Скилл включает comprehensive guide по D3 scales:
Quantitative:
- d3.scaleLinear() — линейная
- d3.scaleLog() — логарифмическая
- d3.scalePow() — степенная
- d3.scaleTime() — временная
Ordinal:
- d3.scaleBand() — для bar charts
- d3.scalePoint() — для line/scatter категорий
- d3.scaleOrdinal() — для цветов
Sequential:
- d3.scaleSequential() — градиент цветов
- d3.scaleDiverging() — diverging palette
Пример:
// Band scale для bar chart
const xScale = d3.scaleBand()
.domain(['Q1', 'Q2', 'Q3', 'Q4'])
.range([0, 400])
.padding(0.1);
// Sequential для heatmap
const colorScale = d3.scaleSequential(d3.interpolateBlues)
.domain([0, 100]);
// Ordinal для категорий
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);Best Practices от скилла
Data preparation:
// Валидация данных
const cleanData = data.filter(d => d.value != null && !isNaN(d.value));
// Парсинг дат
const parseDate = d3.timeParse("%Y-%m-%d");
const parsedData = data.map(d => ({...d, date: parseDate(d.date)}));Performance для больших datasets:
// Canvas instead of SVG для >1000 элементов
// Quadtree для collision detection
// Virtual scrolling для больших списков
// requestAnimationFrame для кастомных анимацийAccessibility:
svg.attr("role", "img")
.attr("aria-label", "Bar chart showing quarterly revenue");
svg.append("title").text("Quarterly Revenue 2024");
svg.append("desc").text("Revenue growth across quarters");Интеграция с фреймворками
React Hook:
function useD3(renderChartFn, dependencies) {
const ref = useRef();
useEffect(() => {
renderChartFn(d3.select(ref.current));
}, dependencies);
return ref;
}
// В компоненте
const chartRef = useD3(
(svg) => drawChart(svg, data),
[data]
);
return <svg ref={chartRef}></svg>;Vue Composition API:
import { ref, onMounted, watch } from 'vue';
export default {
setup() {
const chartRef = ref(null);
const drawChart = () => {
const svg = d3.select(chartRef.value);
// D3 code here...
};
onMounted(drawChart);
watch(() => data.value, drawChart);
return { chartRef };
}
};Svelte:
<script>
import * as d3 from 'd3';
export let data;
let chartElement;
$: if (chartElement && data) {
const svg = d3.select(chartElement);
// D3 code here...
}
</script>
<svg bind:this={chartElement}></svg>Когда использовать D3.js
Use D3.js for:
- Кастомные визуализации, недоступные в готовых библиотеках
- Интерактивные исследования с pan/zoom/brush
- Network/graph визуализации
- Географические карты с кастомными проекциями
- Публикационно-качественная графика
- Анимированные storytelling визуализации
Consider alternatives:
- Chart.js/Highcharts** — для стандартных charts
- Three.js** — для 3D визуализаций
- Observable Plot** — для быстрых exploratory visualizations
- Plotly** — для scientific plotting
Заключение
Claude D3.js Skill — это мастер-класс по созданию уникальных, интерактивных визуализаций. Вместо борьбы с ограничениями готовых библиотек вы получаете полный контроль над каждым пикселем.
Что делает его особенным:
- Comprehensive documentation с living examples
- Готовые templates и boilerplate
- Integration patterns для всех популярных фреймворков
- Performance optimizations для больших datasets
- Accessibility guidelines
GitHub: https://github.com/chrisvoncsefalvay/claude-d3js-skill
Установите скилл, и ваш Claude Code станет D3.js-мастером, способным создавать визуализации уровня New York Times или Observable.
*Data-driven documents: когда данные становятся искусством.* 🧪
> Пока нет комментариев