上一篇: 南丁格尔图 https://blog.csdn.net/zjw_python/article/details/98480980
下一篇: 多符号散点图 https://blog.csdn.net/zjw_python/article/details/98483989
代码结构和初始化画布的Chart对象介绍,请先看 https://blog.csdn.net/zjw_python/article/details/98182540
本图完整的源码地址: https://github.com/zjw666/D3_demo/tree/master/src/scatterChart/basicScatterChart
1 图表效果
2 数据
x,y
24,12
39,20
78,15
15,80
36,70
7,11
36,13
34,89
9,95
72,35
26,65
16,36
99,65
46,78
85,42
32,14
43,12
73,95
8,6
81,62
3 关键代码
导入数据
d3.csv('./data.csv', function(d){
return {
x: +d.x,
y: +d.y
};
}).then(function(data){
....
一些样式参数配置
const config = {
pointColor: chart._colors(0),
margins: {top: 80, left: 80, bottom: 50, right: 80},
textColor: 'black',
gridColor: 'gray',
ShowGridX: [10, 20, 30, 40, 50, 60, 70 ,80, 90, 100],
ShowGridY: [10, 20, 30, 40, 50, 60, 70 ,80, 90, 100],
title: '散点图',
pointSize: 5,
hoverColor: 'white',
animateDuration: 1000
}
尺度转换,与基础柱状图类似,基础散点图也有X和Y两个轴,且两个轴都是连续的类型
/* ----------------------------尺度转换------------------------ */
chart.scaleX = d3.scaleLinear()
.domain([0, Math.ceil(d3.max(data, (d) => d.x)/10)*10])
.range([0, chart.getBodyWidth()])
chart.scaleY = d3.scaleLinear()
.domain([0, Math.ceil(d3.max(data, (d) => d.y)/10)*10])
.range([chart.getBodyHeight(), 0])
渲染数据点,很简单,用circle
元素就可以了
/* ----------------------------渲染数据点------------------------ */
chart.renderPoints = function(){
let points = chart.body().selectAll('.point')
.data(data);
points.enter()
.append('circle')
.classed('point', true)
.merge(points)
.attr('cx', (d) => chart.scaleX(d.x))
.attr('cy', (d) => chart.scaleY(d.y))
.attr('r', 0)
.attr('fill', config.pointColor)
.transition().duration(config.animateDuration)
.attr('r', config.pointSize)
points.exit()
.remove();
}
渲染坐标轴、文本标签和网格,这些与基础柱状图几乎相同
/* ----------------------------渲染坐标轴------------------------ */
chart.renderX = function(){
chart.svg().insert('g','.body')
.attr('transform', 'translate(' + chart.bodyX() + ',' + (chart.bodyY() + chart.getBodyHeight()) + ')')
.attr('class', 'xAxis')
.call(d3.axisBottom(chart.scaleX));
}
chart.renderY = function(){
chart.svg().insert('g','.body')
.attr('transform', 'translate(' + chart.bodyX() + ',' + chart.bodyY() + ')')
.attr('class', 'yAxis')
.call(d3.axisLeft(chart.scaleY));
}
chart.renderAxis = function(){
chart.renderX();
chart.renderY();
}
/* ----------------------------渲染文本标签------------------------ */
chart.renderText = function(){
d3.select('.xAxis').append('text')
.attr('class', 'axisText')
.attr('x', chart.getBodyWidth())
.attr('y', 0)
.attr('fill', config.textColor)
.attr('dy', 30)
.text('X');
d3.select('.yAxis').append('text')
.attr('class', 'axisText')
.attr('x', 0)
.attr('y', 0)
.attr('fill', config.textColor)
.attr('dx', '-30')
.attr('dy', '10')
.text('Y');
}
/* ----------------------------渲染网格线------------------------ */
chart.renderGrid = function(){
d3.selectAll('.yAxis .tick')
.each(function(d, i){
if (config.ShowGridY.indexOf(d) > -1){
d3.select(this).append('line')
.attr('class','grid')
.attr('stroke', config.gridColor)
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', chart.getBodyWidth())
.attr('y2', 0);
}
});
d3.selectAll('.xAxis .tick')
.each(function(d, i){
if (config.ShowGridX.indexOf(d) > -1){
d3.select(this).append('line')
.attr('class','grid')
.attr('stroke', config.gridColor)
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', 0)
.attr('y2', -chart.getBodyHeight());
}
});
}
绑定鼠标交互事件,鼠标悬停时显示坐标值
/* ----------------------------绑定鼠标交互事件------------------------ */
chart.addMouseOn = function(){
//防抖函数
function debounce(fn, time){
let timeId = null;
return function(){
const context = this;
const event = d3.event;
timeId && clearTimeout(timeId)
timeId = setTimeout(function(){
d3.event = event;
fn.apply(context, arguments);
}, time);
}
}
d3.selectAll('.point')
.on('mouseover', function(d){
const e = d3.event;
const position = d3.mouse(chart.svg().node());
e.target.style.cursor = 'hand'
d3.select(e.target)
.attr('fill', config.hoverColor);
chart.svg()
.append('text')
.classed('tip', true)
.attr('x', position[0]+5)
.attr('y', position[1])
.attr('fill', config.textColor)
.text('x: ' + d.x + ', y: ' + d.y);
})
.on('mouseleave', function(){
const e = d3.event;
d3.select(e.target)
.attr('fill', chart._colors(0));
d3.select('.tip').remove();
})
.on('mousemove', debounce(function(){
const position = d3.mouse(chart.svg().node());
d3.select('.tip')
.attr('x', position[0]+5)
.attr('y', position[1]-5);
}, 6)
);
}