d3-v7 数据可视化折线图+悬浮窗

css 复制代码
#TestD3{
  width: 100%;
  height: 35%;
  position: relative;

  .tooltip {
    opacity: 0;
    position: absolute;
    padding: 0.6em 1em;
    background: #fff;
    text-align: center;
    border: 1px solid #ddd;
    z-index: 10;
    transition: all 0.2s ease-out;
    pointer-events: none;
    transform: translate(-50%, calc(-100% - 14px));
  }

  .tooltip:before {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    width: 12px;
    height: 12px;
    background: white;
    border: 1px solid #ddd;
    border-top-color: transparent;
    border-left-color: transparent;
    transform: translate(-50%, 50%) rotate(45deg);
    transform-origin: center center;
    z-index: 10;
  }

  .tooltip-range {
    margin-bottom: 0.2em;
    font-weight: 600;
  }
}
javascript 复制代码
import './index.less';
import * as d3 from 'd3';
import { useEffect, useRef} from 'react';
import useAppState from "../../store";

const TestD3 = () => {
    const svgRef = useRef(null); // 用于保存 SVG 元素的引用
    const containerRef = useRef(null); // 用于保存父节点的引用
    const {testData}=useAppState();
    const draw = (width:number, height:number,dataset: any[]) => {
         // 每次绘制前清除旧图表
        d3.select('#TestD3').select('svg').remove();

        let padding = 30;
        const tooltip = d3.select('#tooltip');

        let xScale = d3
            .scaleBand()
            .domain(d3.range(dataset.length))
            .range([padding, width - padding])
            .padding(0) // 设置柱子之间的间隙
            .paddingInner(1); // 设置柱子内部的间隙

        let yScale = d3
            .scaleLinear()
            .domain([0, d3.max(dataset, function (d) {
                return d.value;
            })])
            .range([height - padding, padding]);

        // 创建 SVG 元素并保存引用
        const svg = d3
            .select('#TestD3')
            .append('svg')
            .attr('width', width)
            .attr('height', height)

        svgRef.current = svg.node(); // 保存 SVG 元素的引用

        let line = d3
            .line()
            .x(function (d, i) {
                return xScale(i) + xScale.bandwidth() / 2;
            })
            .y(function (d) {
                return yScale(d.value);
            })
            .curve(d3.curveCardinal);

        svg
            .append('path')
            .datum(dataset)
            .attr('class', 'line')
            .attr('d', line)
            .attr('fill', 'none')
            .attr('stroke', '#69b3a2')
            .attr('stroke-width', '3px');

        let xAxis = d3
            .axisBottom(xScale)
            .tickFormat(function (d, i) {
                return dataset[i].name;
            });

        let yAxis = d3.axisLeft(yScale);

        svg
            .append('g')
            .attr('class', 'x-axis')
            .attr('transform', 'translate(0,' + (height - padding) + ')')
            .call(xAxis);

        svg
            .append('g')
            .attr('class', 'y-axis')
            .attr('transform', 'translate(' + padding + ',0)')
            .call(yAxis);

        // 绘制圆点
        svg
            .selectAll('.circle')
            .data(dataset)
            .enter()
            .append('circle')
            .attr('class', 'circle')
            .attr('cx', function (d, i) {
                return xScale(i) + xScale.bandwidth() / 2;
            })
            .attr('cy', function (d) {
                return yScale(d.value);
            })
            .attr('r', 4)
            .attr('fill', '#ffffff')
            .attr('stroke', '#69b3a2')
            .attr('stroke-width', '3px');

        // 每个区域绘制一个矩形用于触发事件
        let _w = (width - padding * 2) / (dataset.length - 1);
        svg
            .selectAll('.rect')
            .data(dataset)
            .enter()
            .append('rect')
            .attr('class', 'rect')
            .attr('x', function (d, i) {
                return xScale(i) - _w / 2;
            })
            .attr('y', padding)
            .attr('width', _w)
            .attr('height', height - padding * 2)
            .attr('fill', 'transparent')
            .on('mouseover', function (e,d) {
                console.log(e,d)
                tooltip.style('opacity', 1);
                tooltip.select('#range').text(d.name);
                tooltip.select('#count').text(d.value);
                tooltip
                    .style('left', xScale(d.index) + xScale.bandwidth() / 2  + 'px')
                    .style('top', yScale(d.value)  + 'px');
            })
            .on('mouseout', function (d) {
                tooltip.style('opacity', 0);
            });
    };


    useEffect(() => {
        if(containerRef.current){
            const { width, height } = containerRef.current.getBoundingClientRect();
            let dataset = [
                { name: '苹果', value: 50,index:0 },
                { name: '橙子', value: 30,index:1 },
                { name: '香蕉', value: 70,index:2 },
                { name: '核桃', value: 20,index:3 },
                { name: '芒果', value: 60,index:4 },
                { name: '梨子', value: 100,index:5 },
                { name: '菠萝', value: 80,index:6 },
                { name: '葡萄', value: 90,index:7 },
                { name: '草莓', value: 35,index:8 },
                { name: '西瓜', value: 75,index:9 },
                { name: '桃子', value: 55,index:10 },
                { name: '樱桃', value: 25,index:11 },
            ];
            dataset.forEach(function (d) {
                d.value = Math.floor(Math.random() * 80) + 20;
            });
            draw(width,height,dataset);
        }
    }, [testData]);
    return (
        <>
            <div id="TestD3" ref={containerRef}>
                <div id="tooltip" className="tooltip">
                    <div className="tooltip-range">
                        Name: <span id="range"></span>
                    </div>
                    <div className="tooltip-value">
                        Value: <span id="count"></span>
                    </div>
                </div>
            </div>
        </>
    );
};

export default TestD3;
相关推荐
Teable任意门互动11 小时前
优锘科技北京客户管理数字化落地案例|HAP明道云搭建客户管理平台-任意门互动科技(HAP 官方实施服务商)
大数据·人工智能·科技·信息可视化·信息与通信
xiami_world2 天前
Xmind、Miro、博思白板AI怎么选?思维导图/流程图自动生成
人工智能·ai·信息可视化·流程图·xmind
Highcharts.js3 天前
用Highcharts开发动态主从图表|双图联动的时间序列图表示例
javascript·数据库·信息可视化·highcharts·图表开发·动态图表
智圣新创014 天前
锚定省级示范高职育人评价核验要求 第二课堂数字化落地全场景高频答疑
信息可视化·数据挖掘·数据分析
计算机毕业设计杰瑞4 天前
【2027大数据精品毕设】基于大数据的高频电力消耗数据可视化与分析,附源码_数据可视化_数据分析_毕设选题_开题ppt_大数据项目_文档指导
大数据·信息可视化·课程设计
Software攻城狮4 天前
【cesium 扩展 —— 天地图】
信息可视化
珠海西格电力5 天前
AI预测与优化:基于机器学习的负荷预测与调度策略
大数据·人工智能·机器学习·信息可视化·架构·能源
计算机毕业设计杰瑞6 天前
【精品大数据项目】基于大数据的药品多维度属性分析与可视化的设计与实现,附源码_数据可视化_数据分析_毕设选题推荐_SPark_Hadoop_文档指导ppt
大数据·信息可视化·数据分析
科创致远7 天前
科创致远 ESD 静电监控系统实景效果展示
信息可视化
weixin_440401697 天前
质朴的可视化绘图+pyecharts
开发语言·python·信息可视化·pyecharts