如何用D3.js轻松绘制吸睛散点图?
- 行业动态
- 2025-04-23
- 2
D3.js通过数据驱动文档生成动态散点图,使用SVG元素将二维数据映射为坐标点,开发者可通过比例尺转换数据集,添加坐标轴与交互效果,自定义点样式展示变量间关系,适合数据探索与可视化分析。
散点图是数据可视化中最常见的图表类型之一,能够直观展现两个变量之间的关系,D3.js作为专业级数据可视化库,提供了高度灵活的实现方式,以下将通过可运行的完整代码,演示如何创建符合现代网页标准的交互式散点图。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .axis path { stroke: #718096; } .axis text { fill: #4a5568; font-size: 12px; } .dot:hover { stroke: #2d3748; stroke-width: 2px; } .tooltip { position: absolute; padding: 8px; background: rgba(255, 255, 255, 0.9); border: 1px solid #e2e8f0; border-radius: 4px; pointer-events: none; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } </style> </head> <body> <div id="scatter-plot"></div> <script> // 模拟数据(支持中文字段) const dataset = Array.from({length: 50}, () => ({ 价格: Math.random() * 500 + 100, 销量: Math.random() * 200 + 50, 类别: ['A','B','C'][Math.floor(Math.random()*3)] })); // 容器尺寸 const width = 800, height = 500, padding = {top: 40, right: 30, bottom: 50, left: 60}; // 创建SVG容器 const svg = d3.select("#scatter-plot") .append("svg") .attr("viewBox", `0 0 ${width} ${height}`) .attr("preserveAspectRatio", "xMidYMid meet"); // 创建比例尺 const xScale = d3.scaleLinear() .domain([0, d3.max(dataset, d => d.价格)]) .range([padding.left, width - padding.right]); const yScale = d3.scaleLinear() .domain([0, d3.max(dataset, d => d.销量)]) .range([height - padding.bottom, padding.top]); // 坐标轴 const xAxis = d3.axisBottom(xScale).tickFormat(d => `¥${d}`); const yAxis = d3.axisLeft(yScale); svg.append("g") .attr("class", "axis") .attr("transform", `translate(0, ${height - padding.bottom})`) .call(xAxis); svg.append("g") .attr("class", "axis") .attr("transform", `translate(${padding.left}, 0)`) .call(yAxis); // 创建数据点 const dots = svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("class", "dot") .attr("cx", d => xScale(d.价格)) .attr("cy", d => yScale(d.销量)) .attr("r", 6) .attr("fill", d => ({A:'#63b3ed', B:'#48bb78', C:'#f6ad55'}[d.类别])) .attr("opacity", 0.8); // 添加交互提示 const tooltip = d3.select("body").append("div").attr("class", "tooltip").style("opacity", 0); dots.on("mouseover", (event, d) => { tooltip.transition().duration(200).style("opacity", 0.9) .html(`类别:${d.类别}<br>价格:¥${d.价格.toFixed(2)}<br>销量:${d.销量.toFixed(0)}件`); }) .on("mousemove", (event) => { tooltip.style("left", (event.pageX + 10) + "px") .style("top", (event.pageY - 28) + "px"); }) .on("mouseout", () => { tooltip.transition().duration(500).style("opacity", 0); }); // 添加动画效果 dots.transition() .delay((d,i) => i * 20) .duration(800) .attr("r", 8) .transition() .attr("r", 6); </script> </body> </html>
关键技术解析:
响应式设计
通过viewBox
属性和preserveAspectRatio
实现图形自适应,确保在不同屏幕尺寸下保持比例,移动端设备会按容器宽度自动缩放,无需媒体查询。数据映射
使用scaleLinear()
创建线性比例尺,将原始数据映射到像素坐标,价格字段添加人民币符号格式化显示,增强数据可读性。视觉编码
- 颜色编码:不同类别使用不同填充色
- 大小编码:初始半径6px,悬停时通过动画放大
- 透明度:设置0.8透明度避免重叠点完全遮挡
- 交互体验
- 精确的数据提示框显示完整信息
- 平滑的淡入淡出动画
- 鼠标跟随定位
- 点元素的悬停反馈效果
- 性能优化
- 使用requestAnimationFrame实现平滑动画
- 通过数据绑定(data join)高效更新元素
- 减少DOM操作次数
扩展建议:
- 添加刷选(Brushing)功能实现数据过滤
- 集成ECharts等图表库实现双坐标系
- 增加数据更新时的过渡动画
- 支持CSV/JSON动态数据加载
引用说明
本文代码实现基于D3.js官方文档(https://d3js.org/)最佳实践,颜色方案参考AntV设计规范,SVG坐标系实现参考MDN Web Docs(https://developer.mozilla.org/),所有示例代码可直接在生产环境使用,建议根据实际需求调整视觉样式和交互细节。