如何使用d3.js v6实现点击触发的逐年自动过渡散点图
D3.js v6 散点图年度自动过渡实现方案
你要的效果核心由三部分组成:年份数组预处理、散点更新过渡函数、自动轮播定时器逻辑,具体实现如下:
1. 基础初始化代码
首先是页面结构和基础D3配置,你可以直接套自己的散点图尺寸、比例尺:
<!-- 页面元素 --> <button id="playBtn">开始按年份切换</button> <svg id="scatterPlot" width="800" height="600"></svg>
// 假设你的原始数据格式为 [{x: 数值, y: 数值, year: 2000}, ...] const rawData = 你的全量数据集; // 第一步:提取所有不重复年份并升序排序 const allYears = Array.from(new Set(rawData.map(d => d.year))).sort((a,b) => a - b); let currentYearIndex = 0; // 记录当前展示的年份索引,初始是第一个年份 let playTimer = null; // 存轮播定时器id,方便后续控制 // 初始化svg、比例尺,这里用你自己的业务配置就行 const svg = d3.select('#scatterPlot'); const margin = {top: 20, right: 20, bottom: 30, left: 40}; const width = +svg.attr('width') - margin.left - margin.right; const height = +svg.attr('height') - margin.top - margin.bottom; const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`); // x/y比例尺,根据你的数据值域调整 const xScale = d3.scaleLinear() .domain(d3.extent(rawData, d => d.x)) .range([0, width]); const yScale = d3.scaleLinear() .domain(d3.extent(rawData, d => d.y)) .range([height, 0]); // 初始化坐标轴,只需要渲染一次 g.append('g') .attr('transform', `translate(0,${height})`) .call(d3.axisBottom(xScale)); g.append('g') .call(d3.axisLeft(yScale));
2. 核心更新函数(带过渡效果)
遵循D3的enter/update/exit模式实现年份切换的平滑过渡:
function updateChart(targetYear) { // 过滤出当前年份的所有数据 const yearData = rawData.filter(d => d.year === targetYear); // 选择所有现有散点,第二个参数为数据唯一标识,没有独立id可以用d.x+''+d.y组合,避免过渡错乱 const circles = g.selectAll('circle.scatter-point') .data(yearData, d => d.id); // 退出态:上一年有、当前年没有的散点,淡出后移除 circles.exit() .transition() .duration(1000) // 过渡时长,单位ms .attr('opacity', 0) .remove(); // 进入态:当前年新增的散点,初始透明+位置偏移,淡入到对应位置 const newCircles = circles.enter() .append('circle') .attr('class', 'scatter-point') .attr('r', 4) .attr('fill', 'steelblue') .attr('opacity', 0) .attr('cx', d => xScale(d.x)) .attr('cy', height) // 初始位置可自定义,也可以设为上一年的坐标 // 更新态:两年都有的散点,平滑移动到新位置 newCircles.merge(circles) .transition() .duration(1000) .attr('opacity', 1) .attr('cx', d => xScale(d.x)) .attr('cy', d => yScale(d.y)); } // 页面初始化先渲染第一个年份的数据 updateChart(allYears[currentYearIndex]);
3. 按钮点击自动轮播逻辑
d3.select('#playBtn').on('click', function() { // 避免重复点击生成多个定时器,点击先清掉现有定时器 if (playTimer) clearInterval(playTimer); // 重置索引,从第一个年份开始轮播,不需要重置可以删掉这行 currentYearIndex = 0; updateChart(allYears[currentYearIndex]); // 每隔1.5s切下一个年份,间隔可以自行调整,注意要大于过渡的1000ms避免动画冲突 playTimer = setInterval(() => { currentYearIndex++; // 所有年份播完就停止 if (currentYearIndex >= allYears.length) { clearInterval(playTimer); playTimer = null; return; } updateChart(allYears[currentYearIndex]); }, 1500); });
常见调整项
- 过渡效果可自定义,比如散点大小、颜色变化,直接写在transition后的attr配置里即可
- 需要暂停功能的话,新增按钮点击事件调用
clearInterval(playTimer)即可 - 年份不连续的情况不需要额外处理,预处理时已经做了去重排序,会按实际存在的年份顺序轮播
内容的提问来源于stack exchange,提问作者RipeDude
相关产品推荐
相关产品推荐

