You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Chart.js实现联动图表(饼图筛选折线图数据)

实现Chart.js饼图与折线图的联动筛选

没问题,我直接给你一个可以跑的实用案例,完全适配你的需求——点击饼图的类别扇区,折线图就只展示对应类别下的商品价格走势。

核心思路

  • 先把原始数据做分组整理,方便后续快速筛选
  • 初始化饼图展示所有商品类别
  • 初始化折线图默认显示全量数据
  • 给饼图绑定点击事件,触发时过滤对应类别的数据,更新折线图

完整代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Chart联动示例</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
    <div style="display: flex; gap: 20px; padding: 20px;">
        <div style="width: 400px;">
            <h3>商品类别筛选</h3>
            <canvas id="categoryPieChart"></canvas>
        </div>
        <div style="width: 600px;">
            <h3>价格走势</h3>
            <canvas id="priceLineChart"></canvas>
        </div>
    </div>

    <script>
        // 你的原始数据
        const rawData = [
            {"date": "06/01/2023 16:45", "item": "Apple", "cat": "Fruit", "price": "9.00"},
            {"date": "06/01/2023 16:45", "item": "Orange", "cat": "Fruit", "price": "8.00"},
            {"date": "06/01/2023 16:45", "item": "Pear", "cat": "Fruit", "price": "7.00"},
            {"date": "06/01/2023 16:45", "item": "Water", "cat": "Drink", "price": "2.00"},
            {"date": "06/01/2023 16:45", "item": "Soda", "cat": "Drink", "price": "3.00"},
            {"date": "07/01/2023 16:45", "item": "Apple", "cat": "Fruit", "price": "9.50"},
            {"date": "07/01/2023 16:45", "item": "Orange", "cat": "Fruit", "price": "8.50"},
            {"date": "07/01/2023 16:45", "item": "Pear", "cat": "Fruit", "price": "7.50"},
            {"date": "07/01/2023 16:45", "item": "Water", "cat": "Drink", "price": "2.50"},
            {"date": "07/01/2023 16:45", "item": "Soda", "cat": "Drink", "price": "3.50"}
        ];

        // 第一步:预处理数据,方便后续使用
        // 1. 提取所有唯一日期并排序
        const allDates = [...new Set(rawData.map(item => item.date))].sort((a, b) => new Date(a) - new Date(b));
        // 2. 按类别分组数据
        const groupedByCategory = rawData.reduce((acc, item) => {
            if (!acc[item.cat]) acc[item.cat] = [];
            acc[item.cat].push(item);
            return acc;
        }, {});
        // 3. 按商品分组,整理每个商品的价格时间序列
        const groupedByItem = rawData.reduce((acc, item) => {
            if (!acc[item.item]) {
                acc[item.item] = {
                    cat: item.cat,
                    prices: allDates.map(date => {
                        const match = rawData.find(d => d.item === item.item && d.date === date);
                        return match ? parseFloat(match.price) : null;
                    })
                };
            }
            return acc;
        }, {});

        // 第二步:初始化饼图
        const pieCtx = document.getElementById('categoryPieChart').getContext('2d');
        const pieChart = new Chart(pieCtx, {
            type: 'pie',
            data: {
                labels: Object.keys(groupedByCategory),
                datasets: [{
                    label: '商品数量',
                    data: Object.values(groupedByCategory).map(items => items.length),
                    backgroundColor: ['#FF6384', '#36A2EB'],
                    hoverOffset: 4
                }]
            },
            options: {
                responsive: true,
                // 关键:饼图点击事件
                onClick: (e, elements) => {
                    if (elements.length === 0) return;
                    // 获取点击的类别
                    const clickedCategory = pieChart.data.labels[elements[0].index];
                    // 更新折线图数据
                    updateLineChart(clickedCategory);
                }
            }
        });

        // 第三步:初始化折线图(默认显示全量数据)
        const lineCtx = document.getElementById('priceLineChart').getContext('2d');
        let lineChart = new Chart(lineCtx, {
            type: 'line',
            data: {
                labels: allDates,
                datasets: Object.entries(groupedByItem).map(([itemName, data], index) => ({
                    label: itemName,
                    data: data.prices,
                    borderColor: index % 2 === 0 ? '#FF6384' : '#36A2EB',
                    fill: false,
                    tension: 0.1
                }))
            },
            options: {
                responsive: true,
                scales: {
                    y: {
                        beginAtZero: true,
                        title: {
                            display: true,
                            text: '价格'
                        }
                    },
                    x: {
                        title: {
                            display: true,
                            text: '日期'
                        }
                    }
                }
            }
        });

        // 第四步:定义折线图更新函数
        function updateLineChart(selectedCategory) {
            // 筛选出对应类别的商品
            const filteredItems = Object.entries(groupedByItem).filter(([_, data]) => data.cat === selectedCategory);
            // 生成新的数据集
            const newDatasets = filteredItems.map(([itemName, data], index) => ({
                label: itemName,
                data: data.prices,
                borderColor: selectedCategory === 'Fruit' ? '#FF6384' : '#36A2EB',
                fill: false,
                tension: 0.1
            }));
            // 更新折线图
            lineChart.data.datasets = newDatasets;
            lineChart.update();
        }
    </script>
</body>
</html>

关键细节说明

  • 数据预处理:提前把数据按类别、商品分组,避免每次点击都重新遍历全量数据,提升性能
  • 饼图点击事件:通过onClick回调获取点击的扇区索引,进而拿到对应的类别
  • 折线图更新:调用chart.update()方法实现数据刷新,这是Chart.js更新图表的标准方式
  • 样式适配:给不同类别的折线设置了不同颜色,方便区分

你可以直接把这段代码保存成HTML文件打开就能看到效果,点击饼图的「Fruit」或「Drink」,折线图就会自动切换对应类别的商品数据啦。

内容的提问来源于stack exchange,提问作者douglaslps

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 17:10:50