d3-viz
npx skills add https://github.com/lionad-morotar/claude-d3js-skill-cn --skill d3-viz
Agent 安装分布
Skill 文档
D3.js å¯è§å
æ¦è¿°
æ¤æè½æä¾å ³äºä½¿ç¨ d3.js å建é«çº§äº¤äºå¼æ°æ®å¯è§åçæå¯¼ãD3.jsï¼Data-Driven Documentsï¼æ é¿å°æ°æ®ç»å®å° DOM å ç´ å¹¶å¯¹å®ä»¬åºç¨æ°æ®é©±å¨ç转æ¢ï¼ä»¥åå»ºå ·æå¯¹æ¯ä¸ªè§è§å ç´ ç²¾ç¡®æ§å¶çèªå®ä¹ãåºçè´¨éçå¯è§åææãè¿äºææ¯éç¨äºä»»ä½ JavaScript ç¯å¢ï¼å æ¬åç JavaScriptãReactãVueãSvelte åå ¶ä»æ¡æ¶ã
使¶ä½¿ç¨ d3.js
å¨ä»¥ä¸æ åµä¸ä½¿ç¨ d3.jsï¼
- éè¦ç¬ç¹è§è§ç¼ç æå¸å±çèªå®ä¹å¯è§å
- å ·æå¤æå¹³ç§»ãç¼©æ¾æå·éè¡ä¸ºç交äºå¼æ¢ç´¢
- ç½ç»/å¾å¯è§åï¼å导åå¸å±ãæ ç¶å¾ã屿¬¡ç»æã弦å¾ï¼
- å°çå¯è§åä¸èªå®ä¹æå½±
- éè¦æµç ãåè°è½¬æ¢çå¯è§å
- å ·æç²¾ç»æ ·å¼æ§å¶çåºçè´¨éå¾å½¢
- æ ååºä¸ä¸å¯ç¨çæ°é¢å¾è¡¨ç±»å
对äºä»¥ä¸æ åµèèæ¿ä»£æ¹æ¡ï¼
- 3D å¯è§å – æ¹ç¨ Three.js
æ ¸å¿å·¥ä½æµç¨
1. 设置 d3.js
å¨èæ¬é¡¶é¨å¯¼å ¥ d3ï¼
import * as d3 from 'd3';
æè ä½¿ç¨ CDN çæ¬ï¼7.xï¼ï¼
<script src="https://d3js.org/d3.v7.min.js"></script>
æææ¨¡åï¼æ¯ä¾å°ºãè½´ãå½¢ç¶ãè¿æ¸¡çï¼é½å¯ä»¥éè¿ d3 å½å空é´è®¿é®ã
2. 鿩鿿¨¡å¼
æ¨¡å¼ Aï¼ç´æ¥ DOM æä½ï¼æ¨èç¨äºå¤§å¤æ°æ åµï¼ ä½¿ç¨ d3 éæ© DOM å ç´ å¹¶ä»¥å½ä»¤å¼æ¹å¼æä½å®ä»¬ãè¿éç¨äºä»»ä½ JavaScript ç¯å¢ï¼
function drawChart(data) {
if (!data || data.length === 0) return;
const svg = d3.select('#chart'); // æ IDãç±»æ DOM å
ç´ éæ©
// æ¸
é¤ä¹åçå
容
svg.selectAll("*").remove();
// 设置尺寸
const width = 800;
const height = 400;
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
// å建æ¯ä¾å°ºãè½´åç»å¶å¯è§å
// ... d3 代ç å¨è¿é ...
}
// 卿°æ®æ´æ¹æ¶è°ç¨
drawChart(myData);
æ¨¡å¼ Bï¼å£°æå¼æ¸²æï¼ç¨äºå ·ææ¨¡æ¿çæ¡æ¶ï¼ ä½¿ç¨ 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 æä»¤éåè¿åçæ°ç»
// å¨åç JS ä¸: æ ¹æ®è¿åçæ°æ®æå¨å建å
ç´
对äºå ·æè½¬æ¢ãäº¤äºæå©ç¨ d3 宿´åè½ç夿å¯è§åï¼è¯·ä½¿ç¨æ¨¡å¼ Aãå¯¹äºæ´ç®åçå¯è§åæå½æ¨çæ¡æ¶æ´å欢声æå¼æ¸²ææ¶ï¼è¯·ä½¿ç¨æ¨¡å¼ Bã
3. æå»ºå¯è§å代ç
卿¨çç»å¾å½æ°ä¸éµå¾ªæ¤æ åç»æï¼
function drawVisualization(data) {
if (!data || data.length === 0) return;
const svg = d3.select('#chart'); // æä¼ ééæ©å¨/å
ç´
svg.selectAll("*").remove(); // æ¸
é¤ä¹åçæ¸²æ
// 1. å®ä¹å°ºå¯¸
const width = 800;
const 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. å建带边è·ç主è¦ç»
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// 3. å建æ¯ä¾å°º
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. å建并éå è½´
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
g.append("g")
.attr("transform", `translate(0,${innerHeight})`)
.call(xAxis);
g.append("g")
.call(yAxis);
// 5. ç»å®æ°æ®å¹¶å建è§è§å
ç´
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");
}
// 卿°æ®æ´æ¹æ¶è°ç¨
drawVisualization(myData);
4. å®ç°ååºå¼å°ºå¯¸è°æ´
使å¯è§å对容å¨å¤§å°ååºååºï¼
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);
}
// ç¨æ³ï¼
// const cleanup = setupResponsiveChart('chart-container', myData);
// cleanup(); // å¨ç»ä»¶å¸è½½æå
ç´ ç§»é¤æ¶è°ç¨
æè ä½¿ç¨ ResizeObserver è¿è¡æ´ç´æ¥ç容å¨çæ§ï¼
function setupResponsiveChartWithObserver(svgElement, data) {
const observer = new ResizeObserver(() => {
const { width, height } = svgElement.getBoundingClientRect();
d3.select(svgElement)
.attr('width', width)
.attr('height', height);
// éæ°ç»å¶å¯è§å
drawChart(data, d3.select(svgElement), width, height);
});
observer.observe(svgElement.parentElement);
return () => observer.disconnect();
}
常è§å¯è§å模å¼
æ¡å½¢å¾
function drawBarChart(data, svgElement) {
if (!data || data.length === 0) return;
const svg = d3.select(svgElement);
svg.selectAll("*").remove();
const width = 800;
const 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;
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
const xScale = d3.scaleBand()
.domain(data.map(d => d.category))
.range([0, innerWidth])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([innerHeight, 0]);
g.append("g")
.attr("transform", `translate(0,${innerHeight})`)
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale));
g.selectAll("rect")
.data(data)
.join("rect")
.attr("x", d => xScale(d.category))
.attr("y", d => yScale(d.value))
.attr("width", xScale.bandwidth())
.attr("height", d => innerHeight - yScale(d.value))
.attr("fill", "steelblue");
}
// ç¨æ³ï¼
// drawBarChart(myData, document.getElementById('chart'));
æçº¿å¾
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value))
.curve(d3.curveMonotoneX); // å¹³æ»æ²çº¿
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2)
.attr("d", line);
æ£ç¹å¾
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => xScale(d.x))
.attr("cy", d => yScale(d.y))
.attr("r", d => sizeScale(d.size)) // å¯éï¼å¤§å°ç¼ç
.attr("fill", d => colourScale(d.category)) // å¯éï¼é¢è²ç¼ç
.attr("opacity", 0.7);
弦å¾
弦å¾å¨åå½¢å¸å±ä¸æ¾ç¤ºå®ä½ä¹é´çå ³ç³»ï¼ç¨å¸¦ç¶è¡¨ç¤ºå®ä»¬ä¹é´çæµå¨ï¼
function drawChordDiagram(data) {
// æ°æ®æ ¼å¼ï¼å
嫿ºãç®æ åå¼ç对象æ°ç»
// 示ä¾ï¼[{ source: 'A', target: 'B', value: 10 }, ...]
if (!data || data.length === 0) return;
const svg = d3.select('#chart');
svg.selectAll("*").remove();
const width = 600;
const height = 600;
const innerRadius = Math.min(width, height) * 0.3;
const outerRadius = innerRadius + 30;
// 仿°æ®å建ç©éµ
const nodes = Array.from(new Set(data.flatMap(d => [d.source, d.target])));
const matrix = Array.from({ length: nodes.length }, () => 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;
});
// å建弦å¸å±
const chord = d3.chord()
.padAngle(0.05)
.sortSubgroups(d3.descending);
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
const ribbon = d3.ribbon()
.source(d => d.source)
.target(d => d.target);
const colourScale = d3.scaleOrdinal(d3.schemeCategory10)
.domain(nodes);
const g = svg.append("g")
.attr("transform", `translate(${width / 2},${height / 2})`);
const chords = chord(matrix);
// ç»å¶å¸¦ç¶
g.append("g")
.attr("fill-opacity", 0.67)
.selectAll("path")
.data(chords)
.join("path")
.attr("d", ribbon)
.attr("fill", d => colourScale(nodes[d.source.index]))
.attr("stroke", d => d3.rgb(colourScale(nodes[d.source.index])).darker());
// ç»å¶ç»ï¼å¼§ï¼
const group = g.append("g")
.selectAll("g")
.data(chords.groups)
.join("g");
group.append("path")
.attr("d", arc)
.attr("fill", d => colourScale(nodes[d.index]))
.attr("stroke", d => d3.rgb(colourScale(nodes[d.index])).darker());
// æ·»å æ ç¾
group.append("text")
.each(d => { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", "0.31em")
.attr("transform", d => `rotate(${(d.angle * 180 / Math.PI) - 90})translate(${outerRadius + 30})${d.angle > Math.PI ? "rotate(180)" : ""}`)
.attr("text-anchor", d => d.angle > Math.PI ? "end" : null)
.text((d, i) => nodes[i])
.style("font-size", "12px");
}
çåå¾
çåå¾ä½¿ç¨é¢è²å¨äºç»´ç½æ ¼ä¸ç¼ç å¼ï¼ç¨äºæ¾ç¤ºç±»å«é´ç模å¼ï¼
function drawHeatmap(data) {
// æ°æ®æ ¼å¼ï¼å
å«è¡ãååå¼ç对象æ°ç»
// 示ä¾ï¼[{ row: 'A', column: 'X', value: 10 }, ...]
if (!data || data.length === 0) return;
const svg = d3.select('#chart');
svg.selectAll("*").remove();
const width = 800;
const height = 600;
const margin = { top: 100, right: 30, bottom: 30, left: 100 };
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
// è·åå¯ä¸è¡åå
const rows = Array.from(new Set(data.map(d => d.row)));
const columns = Array.from(new Set(data.map(d => d.column)));
const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// å建æ¯ä¾å°º
const xScale = d3.scaleBand()
.domain(columns)
.range([0, innerWidth])
.padding(0.01);
const yScale = d3.scaleBand()
.domain(rows)
.range([0, innerHeight])
.padding(0.01);
// å¼çé¢è²æ¯ä¾å°º
const colourScale = 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 => colourScale(d.value));
// æ·»å x è½´æ ç¾
svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
.selectAll("text")
.data(columns)
.join("text")
.attr("x", d => xScale(d) + xScale.bandwidth() / 2)
.attr("y", -10)
.attr("text-anchor", "middle")
.text(d => d)
.style("font-size", "12px");
// æ·»å y è½´æ ç¾
svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
.selectAll("text")
.data(rows)
.join("text")
.attr("x", -10)
.attr("y", d => yScale(d) + yScale.bandwidth() / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "end")
.text(d => d)
.style("font-size", "12px");
// æ·»å é¢è²å¾ä¾
const legendWidth = 20;
const legendHeight = 200;
const legend = svg.append("g")
.attr("transform", `translate(${width - 60},${margin.top})`);
const legendScale = d3.scaleLinear()
.domain(colourScale.domain())
.range([legendHeight, 0]);
const legendAxis = d3.axisRight(legendScale)
.ticks(5);
// å¨å¾ä¾ä¸ç»å¶é¢è²æ¸å
for (let i = 0; i < legendHeight; i++) {
legend.append("rect")
.attr("y", i)
.attr("width", legendWidth)
.attr("height", 1)
.attr("fill", colourScale(legendScale.invert(i)));
}
legend.append("g")
.attr("transform", `translate(${legendWidth},0)`)
.call(legendAxis);
}
饼å¾
const pie = d3.pie()
.value(d => d.value)
.sort(null);
const arc = d3.arc()
.innerRadius(0)
.outerRadius(Math.min(width, height) / 2 - 20);
const colourScale = d3.scaleOrdinal(d3.schemeCategory10);
const g = svg.append("g")
.attr("transform", `translate(${width / 2},${height / 2})`);
g.selectAll("path")
.data(pie(data))
.join("path")
.attr("d", arc)
.attr("fill", (d, i) => colourScale(i))
.attr("stroke", "white")
.attr("stroke-width", 2);
å导åç½ç»
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(100))
.force("charge", d3.forceManyBody().strength(-300))
.force("center", d3.forceCenter(width / 2, height / 2));
const link = g.selectAll("line")
.data(links)
.join("line")
.attr("stroke", "#999")
.attr("stroke-width", 1);
const node = g.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", 8)
.attr("fill", "steelblue")
.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);
});
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
æ·»å äº¤äºæ§
å·¥å ·æç¤º
// å建工å
·æç¤º divï¼å¨ SVG å¤é¨ï¼
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background-color", "white")
.style("border", "1px solid #ddd")
.style("padding", "10px")
.style("border-radius", "4px")
.style("pointer-events", "none");
// æ·»å å°å
ç´
circles
.on("mouseover", function(event, d) {
d3.select(this).attr("opacity", 1);
tooltip
.style("visibility", "visible")
.html(`<strong>${d.label}</strong><br/>å¼: ${d.value}`);
})
.on("mousemove", function(event) {
tooltip
.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px");
})
.on("mouseout", function() {
d3.select(this).attr("opacity", 0.7);
tooltip.style("visibility", "hidden");
});
缩æ¾å平移
const zoom = d3.zoom()
.scaleExtent([0.5, 10])
.on("zoom", (event) => {
g.attr("transform", event.transform);
});
svg.call(zoom);
ç¹å»äº¤äº
circles
.on("click", function(event, d) {
// å¤çç¹å»ï¼åæ´¾äºä»¶ãæ´æ°åºç¨ç¨åºç¶æçï¼
console.log("ç¹å»äº:", d);
// è§è§åé¦
d3.selectAll("circle").attr("fill", "steelblue");
d3.select(this).attr("fill", "orange");
// å¯éï¼åæ´¾èªå®ä¹äºä»¶ä¾æ¨çæ¡æ¶/åºç¨ç¨åºçå¬
// window.dispatchEvent(new CustomEvent('chartClick', { detail: d }));
});
}
è¿æ¸¡åå¨ç»
为è§è§ååæ·»å å¹³æ»è¿æ¸¡ï¼
// åºæ¬è¿æ¸¡
circles
.transition()
.duration(750)
.attr("r", 10);
// é¾å¼è¿æ¸¡
circles
.transition()
.duration(500)
.attr("fill", "orange")
.transition()
.duration(500)
.attr("r", 15);
// éå¼è¿æ¸¡
circles
.transition()
.delay((d, i) => i * 50)
.duration(500)
.attr("cy", d => yScale(d.value));
// èªå®ä¹ç¼å¨
circles
.transition()
.duration(1000)
.ease(d3.easeBounceOut)
.attr("r", 10);
æ¯ä¾å°ºåè
å®éæ¯ä¾å°º
// çº¿æ§æ¯ä¾å°º
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
// å¯¹æ°æ¯ä¾å°ºï¼ç¨äºææ°æ°æ®ï¼
const logScale = d3.scaleLog()
.domain([1, 1000])
.range([0, 500]);
// 广¯ä¾å°º
const powScale = d3.scalePow()
.exponent(2)
.domain([0, 100])
.range([0, 500]);
// æ¶é´æ¯ä¾å°º
const timeScale = d3.scaleTime()
.domain([new Date(2020, 0, 1), new Date(2024, 0, 1)])
.range([0, 500]);
åºæ°æ¯ä¾å°º
// 带æ¯ä¾å°ºï¼ç¨äºæ¡å½¢å¾ï¼
const bandScale = d3.scaleBand()
.domain(['A', 'B', 'C', 'D'])
.range([0, 400])
.padding(0.1);
// ç¹æ¯ä¾å°ºï¼ç¨äºæçº¿/æ£ç¹ç±»å«ï¼
const pointScale = d3.scalePoint()
.domain(['A', 'B', 'C', 'D'])
.range([0, 400]);
// åºæ°æ¯ä¾å°ºï¼ç¨äºé¢è²ï¼
const colourScale = d3.scaleOrdinal(d3.schemeCategory10);
åºåæ¯ä¾å°º
// åºåé¢è²æ¯ä¾å°º
const colourScale = d3.scaleSequential(d3.interpolateBlues)
.domain([0, 100]);
// 忣é¢è²æ¯ä¾å°º
const divScale = d3.scaleDiverging(d3.interpolateRdBu)
.domain([-10, 0, 10]);
æä½³å®è·µ
æ°æ®åå¤
å§ç»å¨å¯è§åä¹åéªè¯åå夿°æ®ï¼
// è¿æ»¤æ æå¼
const cleanData = data.filter(d => d.value != null && !isNaN(d.value));
// å¦æé¡ºåºå¾éè¦åæåºæ°æ®
const sortedData = [...data].sort((a, b) => b.value - a.value);
// è§£ææ¥æ
const parsedData = data.map(d => ({
...d,
date: d3.timeParse("%Y-%m-%d")(d.date)
}));
æ§è½ä¼å
对äºå¤§åæ°æ®éï¼>1000 个å ç´ ï¼ï¼
// 使ç¨ç»å¸è䏿¯ SVG ç¨äºè®¸å¤å
ç´
// 使ç¨ååæ è¿è¡ç¢°ææ£æµ
// ä½¿ç¨ d3.line().curve(d3.curveStep) ç®åè·¯å¾
// 为大å表å®ç°èææ»å¨
// ä½¿ç¨ requestAnimationFrame è¿è¡èªå®ä¹å¨ç»
æ éç¢æ§
使å¯è§åå ·ææ éç¢æ§ï¼
// æ·»å ARIA æ ç¾
svg.attr("role", "img")
.attr("aria-label", "æ¾ç¤ºå£åº¦æ¶å
¥çæ¡å½¢å¾");
// æ·»å æ é¢åæè¿°
svg.append("title").text("2024 å¹´å£åº¦æ¶å
¥");
svg.append("desc").text("æ¾ç¤ºå个å£åº¦æ¶å
¥å¢é¿çæ¡å½¢å¾");
// ç¡®ä¿è¶³å¤çé¢è²å¯¹æ¯åº¦
// 为交äºå
ç´ æä¾é®ç导èª
// å
嫿°æ®è¡¨æ¿ä»£æ¹æ¡
æ ·å¼
使ç¨ä¸è´ãä¸ä¸çæ ·å¼ï¼
// æåå®ä¹é¢è²è°è²æ¿
const colours = {
primary: '#4A90E2',
secondary: '#7B68EE',
background: '#F5F7FA',
text: '#333333',
gridLines: '#E0E0E0'
};
// åºç¨ä¸è´çæç
svg.selectAll("text")
.style("font-family", "Inter, sans-serif")
.style("font-size", "12px");
// 使ç¨å¾®å¦çç½æ ¼çº¿
g.selectAll(".tick line")
.attr("stroke", colours.gridLines)
.attr("stroke-dasharray", "2,2");
常è§é®é¢åè§£å³æ¹æ¡
é®é¢ï¼è½´ä¸åºç°
- ç¡®ä¿æ¯ä¾å°ºå ·æææåï¼æ£æ¥ NaN å¼ï¼
- éªè¯è½´éå å°æ£ç¡®çç»
- æ£æ¥åæ¢ç¿»è¯æ¯å¦æ£ç¡®
é®é¢ï¼è¿æ¸¡ä¸èµ·ä½ç¨
- å¨å±æ§æ´æ¹ä¹åè°ç¨
.transition() - ç¡®ä¿å ç´ å ·æå¯ä¸çé®ä»¥è¿è¡éå½çæ°æ®ç»å®
- æ£æ¥ useEffect ä¾èµé¡¹æ¯å¦å å«æææ´æ¹çæ°æ®
é®é¢ï¼ååºå¼å°ºå¯¸è°æ´ä¸èµ·ä½ç¨
- ä½¿ç¨ ResizeObserver æçªå£è°æ´å¤§å°çå¬å¨
- æ´æ°ç¶æä¸ç尺寸以触åéæ°æ¸²æ
- ç¡®ä¿ SVG å ·æå®½åº¦/é«åº¦å±æ§æ viewBox
é®é¢ï¼æ§è½é®é¢
- éå¶ DOM å ç´ çæ°éï¼>1000 项æ¶èèç»å¸ï¼
- é²æè°æ´å¤§å°å¤çç¨åº
- 使ç¨
.join()è䏿¯åç¬çè¿å ¥/æ´æ°/éåºéæ© - éè¿æ£æ¥ä¾èµé¡¹é¿å ä¸å¿ è¦çéæ°æ¸²æ
èµæº
references/
å å«è¯¦ç»çåèèµæï¼
d3-patterns.md– å¯è§å模å¼å代ç 示ä¾ç综åéåscale-reference.md– d3 æ¯ä¾å°ºç宿´æåå示ä¾colour-schemes.md– D3 é¢è²è°è²æ¿åæ¨è
assets/
å 嫿 ·æ¿æ¨¡æ¿ï¼
chart-template.js– åºæ¬å¾è¡¨çèµ·å§æ¨¡æ¿interactive-template.js– 带æå·¥å ·æç¤ºã缩æ¾å交äºç模æ¿sample-data.json– ç¨äºæµè¯çç¤ºä¾æ°æ®é
è¿äºæ¨¡æ¿éç¨äºåç JavaScriptãReactãVueãSvelte æä»»ä½å ¶ä» JavaScript ç¯å¢ãæ ¹æ®æ¨çç¹å®æ¡æ¶éè¦è¿è¡è°æ´ã
è¦ä½¿ç¨è¿äºèµæºï¼å¨éè¦ç¹å®å¯è§åç±»åææ¨¡å¼çè¯¦ç»æå¯¼æ¶ï¼è¯·é 读ç¸å ³æä»¶ã