安装依赖包
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);
});