如何使用D3.js绘制带指定交互动效的自定义柱状图?

实现效果说明
- 页面加载后优先触发黄色柱状的入场动画,动画时长500ms,高度从0过渡到目标值
- 黄色动画完成后延迟600ms,蓝色柱状按索引顺序依次执行入场动画,每个柱子间隔100ms触发
- 点击X轴任意年份标签,会在对应年份位置绘制高亮参考骨线,自动替换上一次点击生成的骨线
完整实现代码
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>D3柱状图实现</title> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .yellow-bar { fill: #ffc107; } .blue-bar { fill: #1976d2; } .axis-label { cursor: pointer; } .guide-line { stroke: #ff0000; stroke-width: 2; stroke-dasharray: 5,5; } </style> </head> <body> <svg width="800" height="500" id="chart"></svg> <script> // 模拟数据,可根据实际需求替换 const data = [ { year: '2018', yellowVal: 120, blueVal: 45 }, { year: '2019', yellowVal: 150, blueVal: 62 }, { year: '2020', yellowVal: 180, blueVal: 78 }, { year: '2021', yellowVal: 220, blueVal: 95 }, { year: '2022', yellowVal: 260, blueVal: 110 }, { year: '2023', yellowVal: 300, blueVal: 135 } ] const margin = { top: 30, right: 30, bottom: 50, left: 50 } const width = 800 - margin.left - margin.right const height = 500 - margin.top - margin.bottom const svg = d3.select('#chart') .append('g') .attr('transform', `translate(${margin.left},${margin.top})`) // 比例尺 const xScale = d3.scaleBand() .domain(data.map(d => d.year)) .range([0, width]) .padding(0.3) const yScale = d3.scaleLinear() .domain([0, d3.max(data, d => d.yellowVal)]) .range([height, 0]) // 绘制X轴 const xAxis = svg.append('g') .attr('transform', `translate(0,${height})`) .call(d3.axisBottom(xScale)) .selectAll('.tick text') .attr('class', 'axis-label') // 绘制黄色柱状 const yellowBars = svg.selectAll('.yellow-bar') .data(data) .join('rect') .attr('class', 'yellow-bar') .attr('x', d => xScale(d.year)) .attr('width', xScale.bandwidth()) .attr('y', height) .attr('height', 0) // 黄色柱入场动画 yellowBars.transition() .duration(500) .attr('y', d => yScale(d.yellowVal)) .attr('height', d => height - yScale(d.yellowVal)) // 绘制蓝色柱状 const blueBars = svg.selectAll('.blue-bar') .data(data) .join('rect') .attr('class', 'blue-bar') .attr('x', d => xScale(d.year)) .attr('width', xScale.bandwidth()) .attr('y', height) .attr('height', 0) // 蓝色柱延迟入场动画 blueBars.transition() .delay((d, i) => 1100 + i * 100) .duration(400) .attr('y', d => yScale(d.blueVal)) .attr('height', d => height - yScale(d.blueVal)) // 点击年份显示骨线逻辑 let activeGuideline = null xAxis.on('click', (event, d) => { // 清除旧骨线 if (activeGuideline) activeGuideline.remove() // 绘制新骨线 const xPos = xScale(d) + xScale.bandwidth() / 2 activeGuideline = svg.append('line') .attr('class', 'guide-line') .attr('x1', xPos) .attr('x2', xPos) .attr('y1', 0) .attr('y2', height) }) </script> </body> </html>
内容的提问来源于stack exchange,提问作者Yashwanth Gurrapu
相关产品推荐
相关产品推荐

