You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何将同一页面中的两个Google Charts导出为单张PNG图片?

嘿,作为Web编程新手,能搞定单个图表导出已经很棒了!合并两个图表其实核心就是把两个图表的可视化内容转成图像,再在一个画布上按顺序拼接,最后导出这个画布。下面给你一套完整的可运行代码,每一步都有注释,跟着来就行~

完整解决方案代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>合并Google Charts导出PNG</title>
    <!-- 加载Google Charts核心库 -->
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script type="text/javascript">
        google.charts.load('current', {'packages':['corechart']});
        google.charts.setOnLoadCallback(drawBothCharts);

        // 全局保存两个图表实例,方便后续获取内容
        let chart1, chart2;

        function drawBothCharts() {
            // 绘制第一个柱状图
            const salesData = google.visualization.arrayToDataTable([
                ['年份', '销量'],
                ['2020', 1000],
                ['2021', 1170],
                ['2022', 660],
                ['2023', 1030]
            ]);
            const salesOptions = {title: '年度销量统计', width: 600, height: 300};
            chart1 = new google.visualization.ColumnChart(document.getElementById('chart1_div'));
            chart1.draw(salesData, salesOptions);

            // 绘制第二个折线图
            const visitData = google.visualization.arrayToDataTable([
                ['月份', '访问量'],
                ['1月', 400],
                ['2月', 300],
                ['3月', 500],
                ['4月', 600],
                ['5月', 700]
            ]);
            const visitOptions = {title: '月度网站访问量', width: 600, height: 300};
            chart2 = new google.visualization.LineChart(document.getElementById('chart2_div'));
            chart2.draw(visitData, visitOptions);
        }

        // 核心函数:合并两个图表并导出为PNG
        function exportCombinedChart() {
            // 1. 提取两个图表的SVG内容(Google Charts默认用SVG渲染)
            const svg1 = chart1.getContainer().querySelector('svg').outerHTML;
            const svg2 = chart2.getContainer().querySelector('svg').outerHTML;

            // 2. 创建Image对象加载SVG内容(SVG不能直接画到Canvas,转成Image才行)
            const img1 = new Image();
            const img2 = new Image();

            // 把SVG转成Data URL供Image加载
            img1.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg1);
            img2.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg2);

            // 3. 等待两个图像都加载完成再执行合并
            Promise.all([
                new Promise(resolve => img1.onload = resolve),
                new Promise(resolve => img2.onload = resolve)
            ]).then(() => {
                // 创建合并用的Canvas
                const combinedCanvas = document.createElement('canvas');
                const ctx = combinedCanvas.getContext('2d');

                // 设置Canvas尺寸:宽度取图表宽度,高度为两个图表高度之和
                combinedCanvas.width = chart1.getContainer().offsetWidth;
                combinedCanvas.height = chart1.getContainer().offsetHeight + chart2.getContainer().offsetHeight;

                // 4. 按页面上下顺序绘制两个图像
                ctx.drawImage(img1, 0, 0); // 第一个图表放在顶部
                ctx.drawImage(img2, 0, chart1.getContainer().offsetHeight); // 第二个图表放在第一个下方

                // 5. 触发PNG下载
                const downloadLink = document.createElement('a');
                downloadLink.href = combinedCanvas.toDataURL('image/png');
                downloadLink.download = 'combined_charts.png';
                downloadLink.click();
            }).catch(err => {
                console.error('合并导出失败:', err);
                alert('导出失败,请查看控制台日志排查问题');
            });
        }
    </script>
</head>
<body>
    <div id="chart1_div" style="margin: 20px auto;"></div>
    <div id="chart2_div" style="margin: 20px auto;"></div>
    <button onclick="exportCombinedChart()" style="display: block; margin: 20px auto;">导出合并图表为PNG</button>
</body>
</html>
关键细节说明
  1. 保存图表实例
    chart1chart2设为全局变量,是为了后续能方便获取它们的渲染容器和SVG内容。

  2. SVG转Image的原因
    Google Charts默认用SVG渲染图表,而Canvas的drawImage方法不支持直接绘制SVG,所以需要先把SVG转成Data URL加载成Image对象。

  3. 异步加载处理
    Promise.all等待两个Image加载完成,避免出现图表还没加载好就绘制导致的空白问题。

  4. Canvas合并逻辑
    严格按照页面上图表的上下排列样式,把第二个图表的绘制起点设为第一个图表的高度,保证导出的PNG和页面显示一致。

额外注意事项
  • 如果你的图表是Canvas渲染类型(少数Google Charts类型会用),可以直接提取Canvas元素替换SVG的步骤,比如const canvas1 = chart1.getContainer().querySelector('canvas'),然后直接drawImage(canvas1, 0, 0)
  • 若部署后遇到跨域问题,可把SVG转成Blob对象再生成URL,替换img.src的代码:
    function svgToBlobUrl(svg) {
        const blob = new Blob([svg], {type: 'image/svg+xml;charset=utf-8'});
        return URL.createObjectURL(blob);
    }
    // 使用方式:img1.src = svgToBlobUrl(svg1);
    
  • 可以给导出按钮加加载状态,或者在图表ready事件后启用按钮,防止用户提前点击:
    // 在drawBothCharts函数里添加
    google.visualization.events.addListener(chart1, 'ready', () => {
        google.visualization.events.addListener(chart2, 'ready', () => {
            document.querySelector('button').disabled = false;
        });
    });
    
    同时给按钮加上disabled="true"属性。

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

火山引擎 最新活动