安装依赖包

npm install d3

基础图形绘制

导入:

import * as d3 from 'd3';

1. 矩形

const svg = d3.select('.d3-box').append('svg').attr('width', 200).attr('height', 200);

svg.append('rect').attr('x', 120).attr('y', 60).attr('width', 80).attr('height', 50).style('fill', 'black');

2. 圆形

const svg = d3.select('.d3-box').append('svg').attr('width', 200).attr('height', 200);

svg.append('circle').attr('cx', 100).attr('cy', 100).attr('r', 50).style('fill', 'black');

圆形可以通过<circle> SVG元素来创建,需要设置cx, cy(圆心坐标)和r(半径)属性

3. 多边形

const svg = d3.select('.d3-box').append('svg').attr('width', 200).attr('height', 200);

svg.append('polygon')
    .attr('points', '10,10 100,10 80,100 10,60')
    .style('fill', 'black');

创建图表

1. 柱状图

// 定义尺寸大小
const margin = { top: 50, right: 50, bottom: 70, left: 70 };
const width = 800 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

// 创建svg
const container = d3.select('.d3-box');
const svg = container
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

const data = [
    { category: 'A', value: 30 },
    { category: 'B', value: 85 },
    { category: 'C', value: 60 },
    { category: 'D', value: 45 },
    { category: 'E', value: 75 },
    { category: 'F', value: 50 }
];

// X轴比例尺
const x = d3
.scaleBand()
.domain(data.map(d => d.category))
.range([0, width])
.padding(0.2);

// Y轴比例尺
const y = d3
.scaleLinear()
.domain([0, d3.max(data, d => d.value) * 1.1])
.range([height, 0]);

// 添加X轴
svg.append('g').attr('transform', `translate(0, ${height})`).call(d3.axisBottom(x)).selectAll('text');

// 添加Y轴
svg.append('g').call(d3.axisLeft(y).ticks(5));

// 添加X轴标签
svg.append('text')
    .attr('x', width / 2)
    .attr('y', height + 50)
    .attr('text-anchor', 'middle')
    .text('类别');

// 添加Y轴标签
svg.append('text')
    .attr('transform', 'rotate(-90)')
    .attr('x', -height / 2)
    .attr('y', -50)
    .attr('text-anchor', 'middle')
    .text('数值');

// 添加标题
svg.append('text')
    .attr('x', width / 2)
    .attr('y', -15)
    .attr('text-anchor', 'middle')
    .text('柱状图示例');

// 创建柱状图
svg.selectAll('.bar')
    .data(data)
    .enter()
    .append('rect')
    .attr('class', 'bar')
    .attr('x', d => x(d.category))
    .attr('y', d => y(d.value))
    .attr('width', x.bandwidth())
    .attr('height', d => height - y(d.value))
    .attr('fill', (d, i) => '#4e89ae');

// 添加数据标签
svg.selectAll('.data-label')
    .data(data)
    .enter()
    .append('text')
    .attr('class', 'data-label')
    .attr('x', d => x(d.category) + x.bandwidth() / 2 - 6)
    .attr('y', d => y(d.value) - 10)
    .text(d => d.value);

2. 折线图

// 定义尺寸大小
const margin = { top: 50, right: 50, bottom: 70, left: 70 };
const width = 800 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

// 创建svg
const container = d3.select('.d3-box');
const svg = container
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

const data = [
    { month: '1月', value: 20 },
    { month: '2月', value: 35 },
    { month: '3月', value: 45 },
    { month: '4月', value: 70 },
    { month: '5月', value: 60 },
    { month: '6月', value: 85 }
];

// X轴比例尺
const x = d3
.scaleBand()
.domain(data.map(d => d.month))
.range([0, width])
.padding(0.5);

// Y轴比例尺
const y = d3
.scaleLinear()
.domain([0, d3.max(data, d => d.value) * 1.1])
.range([height, 0]);

// 添加X轴
svg.append('g').attr('transform', `translate(0, ${height})`).call(d3.axisBottom(x));

// 添加Y轴
svg.append('g').call(d3.axisLeft(y).ticks(5));

// 添加X轴标签
svg.append('text')
    .attr('x', width / 2)
    .attr('y', height + 50)
    .attr('text-anchor', 'middle')
    .text('月份');

// 添加Y轴标签
svg.append('text')
    .attr('transform', 'rotate(-90)')
    .attr('x', -height / 2)
    .attr('y', -50)
    .attr('text-anchor', 'middle')
    .text('数值');

// 添加标题
svg.append('text')
    .attr('x', width / 2)
    .attr('y', -15)
    .attr('text-anchor', 'middle')
    .text('折线图示例');

// 创建折线路径
const line = d3
.line()
.x(d => x(d.month) + x.bandwidth() / 2)
.y(d => y(d.value));

svg.append('path').datum(data).attr('class', 'line').attr('d', line).attr('stroke', '#ff6b6b');

// 添加数据点
svg.selectAll('.dot')
    .data(data)
    .enter()
    .append('circle')
    .attr('class', 'dot')
    .attr('cx', d => x(d.month) + x.bandwidth() / 2)
    .attr('cy', d => y(d.value))
    .attr('r', 6)
    .attr('fill', '#ff6b6b');

// 添加数据标签
svg.selectAll('.data-label')
    .data(data)
    .enter()
    .append('text')
    .attr('class', 'data-label')
    .attr('x', d => x(d.month) + x.bandwidth() / 2 - 6)
    .attr('y', d => y(d.value) - 15)
    .text(d => d.value);

注意添加如下css样式:

.line {
    fill: none;
}

3. 饼图

// 定义尺寸大小
const margin = { top: 50, right: 50, bottom: 70, left: 70 };
const width = 800 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;

// 创建svg
const container = d3.select('.d3-box');
const svg = container
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

const data = [
    { category: '技术', value: 35 },
    { category: '营销', value: 25 },
    { category: '运营', value: 20 },
    { category: '销售', value: 15 },
    { category: '其他', value: 5 }
];
const colors = ['#ff6b6b', '#4ecdc4', '#556270', '#c06c84', '#ffa372']

// 设置饼图半径
const radius = Math.min(width, height) / 2 - 20;

// 创建饼图布局
const pie = d3
.pie()
.value(d => d.value)
.sort(null);

// 弧生成器
const arc = d3.arc().innerRadius(0).outerRadius(radius);

// 标签弧生成器
const labelArc = d3
.arc()
.innerRadius(radius * 0.6)
.outerRadius(radius * 0.6);

// 将SVG容器移动到中心
const g = svg.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`);

// 生成弧
const arcs = g.selectAll('.pie-arc').data(pie(data)).enter().append('g').attr('class', 'pie-arc');

// 绘制弧
arcs.append('path')
    .attr('d', arc)
    .attr('fill', (d, i) => colors[i]);

// 添加数据标签
arcs.append('text')
    .attr('transform', d => `translate(${labelArc.centroid(d)})`)
    .attr('class', 'pie-label')
    .text(d => `${d.data.category}: ${d.data.value}%`);

// 添加标题
svg.append('text')
    .attr('x', width / 2)
    .attr('y', 20)
    .attr('text-anchor', 'middle')
    .text('饼图示例');

css样式如下:

.pie-arc path {
    stroke: #fff;
    stroke-width: 2px;
    transition: transform 0.3s ease;
}

.pie-arc:hover path {
    transform: scale(1.05);
}

.pie-label {
    fill: white;
    font-size: 12px;
    font-weight: bold;
    text-anchor: middle;
    pointer-events: none;
}

添加事件

这里以饼图为例:

// 绘制弧
arcs.append('path')
    .attr('d', arc)
    .attr('fill', (d, i) => colors[i])
    .on('click', function (event, d) {
    console.log('点击了:', d);
});