解决echarts配置滚动(dataZoom)后导出图片数据不全问题

先展现一个echarts,并配置dataZoom,每页最多10条数据,超出滚动

<div class="echartsBox" id="echartsBox"></div>

onMounted(() => {
    nextTick(() => {
        var chartDom = document.getElementById('echartsBox');
        myChart = echarts.init(chartDom);
        option = {
            grid: {
                left: '0px',    // 图表左边距
                right: '50px',   // 图表右边距
                top: '50px',     // 图表上边距
                bottom: '0px',  // 图表下边距
                containLabel: true // 包含坐标轴标签在内
            },
            graphic: [{
                type: 'text',
                left: '15',  // 根据需要调整位置
                top: '20',   // 根据需要调整位置
                z: 100,      // 设置 z 轴数值较高,确保文本显示在最前面
                style: {
                    text: '课程内容',  // 指定要显示的文本
                    fill: '#666666',     // 文本颜色
                    fontSize: '14px',
                }
            }],
            yAxis: {
                // name: '课程内容',
                type: 'category',
                data: ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19', 'A20', 'A21', 'A22', 'A23', 'A24', 'A25'],
                inverse: true,
            },
            xAxis: {
                name: '得分',
                type: 'value',
                min: 5,
                max: 0,
                axisLabel: {
                    formatter: function (value: any) {
                        return Math.floor(value); // 取整数部分
                    }
                },
            },
            dataZoom: [
                {
                    type: "slider",
                    realtime: true, // 拖动时,是否实时更新系列的视图
                    // show:false,  // 是否展示滚动条根据数据判断
                    startValue: 0,
                    endValue: 9, // 最多10条 超出滚动
                    width: 5,
                    height: "75%",
                    top: "12.5%",
                    right: 0,
                    brushSelect: false,
                    yAxisIndex: [0, 1], // 控制y轴滚动
                    fillerColor: "#0093ff", // 滚动条颜色
                    borderColor: "rgba(17, 100, 210, 0.12)",
                    backgroundColor: "#cfcfcf", //两边未选中的滑动条区域的颜色
                    handleSize: 0, // 两边手柄尺寸
                    showDataShadow: false, //是否显示数据阴影 默认auto
                    showDetail: false, // 拖拽时是否展示滚动条两侧的文字
                    zoomLock: true,
                    moveHandleStyle: {
                        opacity: 0,
                    },
                },
                {
                    type: "inside",
                    startValue: 0,
                    endValue: 10,
                    minValueSpan: 10,
                    yAxisIndex: [0],
                    zoomOnMouseWheel: false, // 关闭滚轮缩放
                    moveOnMouseWheel: true, // 开启滚轮平移
                    moveOnMouseMove: true, // 鼠标移动能触发数据窗口平移
                },
            ],
            series: [
                {
                    data: [5, 0, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1],
                    type: 'bar',
                    label: {
                        show: true,
                        position: 'right',
                        formatter: '{c}分', // 显示数值
                        textStyle: {
                            color: '#333',
                            fontSize: 14
                        }
                    },
                    itemStyle: {
                        color: function (params: any) {
                            // 根据数据值取整后选择颜色
                            var value = Math.round(params.data);
                            var colors = ['#A6A6A6', '#FF7F2D', '#FCC946', '#A2C081', '#619C8A', '#016B25'];
                            return colors[value];
                        }
                    },
                }
            ]
        };

        option && myChart.setOption(option);
    })
})

效果:

调用echarts中getDataURL获取图表的数据 URL

// 下载echarts
const downloadBtn = (() => {
    nextTick(() => {
        const loading = ElLoading.service({
            lock: true,
            text: '图表生成中',
            background: 'rgba(0, 0, 0, 0.7)',
        })
        // 需要3s左右生成
        setTimeout(() => {
            loading.close()
            // 获取图表的数据 URL
            var dataURL = myChart.getDataURL({
                type: 'png',  // 可以根据需要修改为其他格式,如 'jpeg'
                pixelRatio: 2,  // 图片分辨率,根据需要进行调整
                backgroundColor: '#fff'  // 图片背景色,根据需要进行调整
            });

            // 创建一个虚拟的下载链接并模拟点击下载
            var link = document.createElement('a');
            link.href = dataURL;
            link.download = 'echarts_image.png';  // 下载的文件名,可以根据需要修改
            link.style.display = 'none';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }, 3000)
    })
})

问题来了:如数据不分页则可以下载全数据,如数据分页了则只能下载出可视区内容,如何解决?

解决思路:echarts最终生成了canvas,canvas的宽高就是当前可视区的宽高,那么是否可以动态计算高度???

解决:

新增一个容器,这个容器为动态计算高度后导出使用

<!-- 导出echarts使用 -->
    <div id="newEchartsBox" style="display:none;"></div>

// 下载echarts
const downloadBtn = (() => {
    // 获取完整的数据
    const fullData = myChart.getOption();
    let newOption = fullData
    newOption.dataZoom = []
    // 
    var chartDom: any = document.getElementById('newEchartsBox');
    chartDom.style.width = '600px'
    chartDom.style.height = 50 * fullData.series[0].data.length + 'px'
    // 
    newMyChart = echarts.init(chartDom);
    // 
    newOption && newMyChart.setOption(newOption);
    // 
    nextTick(() => {
        const loading = ElLoading.service({
            lock: true,
            text: '图表生成中',
            background: 'rgba(0, 0, 0, 0.7)',
        })
        // 需要3s左右生成
        setTimeout(() => {
            loading.close()
            // 获取图表的数据 URL
            var dataURL = newMyChart.getDataURL({
                type: 'png',  // 可以根据需要修改为其他格式,如 'jpeg'
                pixelRatio: 2,  // 图片分辨率,根据需要进行调整
                backgroundColor: '#fff'  // 图片背景色,根据需要进行调整
            });

            // 创建一个虚拟的下载链接并模拟点击下载
            var link = document.createElement('a');
            link.href = dataURL;
            link.download = 'echarts_image.png';  // 下载的文件名,可以根据需要修改
            link.style.display = 'none';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }, 3000)
    })
});

通过getOption获取echarts数据,根据数据长度动态设置新容器的高度,并赋值,赋值时把dataZoom清空,这里就不需要分页了,因为不做回显。

然后通过新容器调用echarts导出图片,问题完美解决。

源码如下:

<template>
    <div class="kcnrzt">
        <div class="left">
            <div class="l">
                已选<br />课程内容
            </div>
            <div class="r">
                <el-scrollbar>
                    <div class="list">
                        <div class="item">11111111111111111111111111111111111111111111111111111111111111111</div>
                        <div class="item">11111111111111111111111111111111111111111111111111111111111111111</div>
                        <div class="item">11111111111111111111111111111111111111111111111111111111111111111</div>
                        <div class="item">11111111111111111111111111111111111111111111111111111111111111111</div>
                        <div class="item">11111111111111111111111111111111111111111111111111111111111111111</div>
                        <div class="item">11111111111111111111111111111111111111111111111111111111111111111</div>
                    </div>
                </el-scrollbar>
            </div>
        </div>
        <div class="right">
            <div class="exportBtn" @click="downloadBtn">导出报告</div>
            <div class="echartsBox" id="echartsBox"></div>
        </div>
    </div>
    <!-- 导出echarts使用 -->
    <div id="newEchartsBox" style="display:none;"></div>
</template>

<script setup lang="ts">
import { ElLoading } from 'element-plus'
import { watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { dataStore } from '@/store'
let store = dataStore()
import * as echarts from 'echarts';

// 
var myChart: any = null
var option: any = null
// 导出专用
var newMyChart: any = null

onMounted(() => {
    nextTick(() => {
        var chartDom = document.getElementById('echartsBox');
        myChart = echarts.init(chartDom);
        option = {
            grid: {
                left: '0px',    // 图表左边距
                right: '50px',   // 图表右边距
                top: '50px',     // 图表上边距
                bottom: '0px',  // 图表下边距
                containLabel: true // 包含坐标轴标签在内
            },
            graphic: [{
                type: 'text',
                left: '15',  // 根据需要调整位置
                top: '20',   // 根据需要调整位置
                z: 100,      // 设置 z 轴数值较高,确保文本显示在最前面
                style: {
                    text: '课程内容',  // 指定要显示的文本
                    fill: '#666666',     // 文本颜色
                    fontSize: '14px',
                }
            }],
            yAxis: {
                // name: '课程内容',
                type: 'category',
                data: ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19', 'A20', 'A21', 'A22', 'A23', 'A24', 'A25'],
                inverse: true,
            },
            xAxis: {
                name: '得分',
                type: 'value',
                min: 5,
                max: 0,
                axisLabel: {
                    formatter: function (value: any) {
                        return Math.floor(value); // 取整数部分
                    }
                },
            },
            dataZoom: [
                {
                    type: "slider",
                    realtime: true, // 拖动时,是否实时更新系列的视图
                    // show:false,  // 是否展示滚动条根据数据判断
                    startValue: 0,
                    endValue: 9, // 最多10条 超出滚动
                    width: 5,
                    height: "75%",
                    top: "12.5%",
                    right: 0,
                    brushSelect: false,
                    yAxisIndex: [0, 1], // 控制y轴滚动
                    fillerColor: "#0093ff", // 滚动条颜色
                    borderColor: "rgba(17, 100, 210, 0.12)",
                    backgroundColor: "#cfcfcf", //两边未选中的滑动条区域的颜色
                    handleSize: 0, // 两边手柄尺寸
                    showDataShadow: false, //是否显示数据阴影 默认auto
                    showDetail: false, // 拖拽时是否展示滚动条两侧的文字
                    zoomLock: true,
                    moveHandleStyle: {
                        opacity: 0,
                    },
                },
                {
                    type: "inside",
                    startValue: 0,
                    endValue: 10,
                    minValueSpan: 10,
                    yAxisIndex: [0],
                    zoomOnMouseWheel: false, // 关闭滚轮缩放
                    moveOnMouseWheel: true, // 开启滚轮平移
                    moveOnMouseMove: true, // 鼠标移动能触发数据窗口平移
                },
            ],
            series: [
                {
                    data: [5, 0, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1],
                    type: 'bar',
                    label: {
                        show: true,
                        position: 'right',
                        formatter: '{c}分', // 显示数值
                        textStyle: {
                            color: '#333',
                            fontSize: 14
                        }
                    },
                    itemStyle: {
                        color: function (params: any) {
                            // 根据数据值取整后选择颜色
                            var value = Math.round(params.data);
                            var colors = ['#A6A6A6', '#FF7F2D', '#FCC946', '#A2C081', '#619C8A', '#016B25'];
                            return colors[value];
                        }
                    },
                }
            ]
        };

        option && myChart.setOption(option);
    })
    // 
    window.addEventListener('resize', updateEcharts);
})
// 
onBeforeUnmount(() => {
    myChart.dispose();
    // 
    window.removeEventListener('resize', updateEcharts);
})
//
const updateEcharts = (() => {
    nextTick(() => {
        myChart.resize();
    })
})
// 
watch(
    () => store.isCollapse,
    () => {
        setTimeout(() => { updateEcharts() }, 300);
    },
    {
        immediate: false,  // 是否初始化立即执行一次, 默认是false
        deep: true // 是否是深度监视, 默认是false
    }
)

// 下载echarts
const downloadBtn = (() => {
    // 获取完整的数据
    const fullData = myChart.getOption();
    let newOption = fullData
    newOption.dataZoom = []
    // 
    var chartDom: any = document.getElementById('newEchartsBox');
    chartDom.style.width = '600px'
    chartDom.style.height = 50 * fullData.series[0].data.length + 'px'
    // 
    newMyChart = echarts.init(chartDom);
    // 
    newOption && newMyChart.setOption(newOption);
    // 
    nextTick(() => {
        const loading = ElLoading.service({
            lock: true,
            text: '图表生成中',
            background: 'rgba(0, 0, 0, 0.7)',
        })
        // 需要3s左右生成
        setTimeout(() => {
            loading.close()
            // 获取图表的数据 URL
            var dataURL = newMyChart.getDataURL({
                type: 'png',  // 可以根据需要修改为其他格式,如 'jpeg'
                pixelRatio: 2,  // 图片分辨率,根据需要进行调整
                backgroundColor: '#fff'  // 图片背景色,根据需要进行调整
            });

            // 创建一个虚拟的下载链接并模拟点击下载
            var link = document.createElement('a');
            link.href = dataURL;
            link.download = 'echarts_image.png';  // 下载的文件名,可以根据需要修改
            link.style.display = 'none';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }, 3000)
    })
});
</script>

<style scoped lang="scss">
.kcnrzt {
    flex: 1;
    display: flex;
    justify-content: space-between;
    overflow: hidden;
    margin: 20px 15px;

    .left {
        width: 50%;
        height: 100%;
        border: 1px solid #DEDEDE;
        display: flex;
        overflow: hidden;
        margin-right: 20px;

        .l {
            width: 87px;
            border-right: 1px solid #DEDEDE;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
            overflow: hidden;
        }

        .r {
            flex: 1;
            overflow: hidden;

            .list {
                .item {
                    padding: 10px;
                    font-size: 14px;
                    font-family: Microsoft YaHei;
                    font-weight: 400;
                    color: #333333;
                    line-height: 22px;
                    border-bottom: 1px solid #DEDEDE;
                }
            }
        }
    }

    .right {
        width: 50%;
        height: 100%;
        position: relative;
        margin-left: 20px;

        .exportBtn {
            width: 81px;
            height: 30px;
            background: #FFB100;
            border-radius: 6px;
            font-size: 14px;
            font-family: Microsoft YaHei;
            font-weight: 400;
            color: #FFFFFF;
            line-height: 30px;
            text-align: center;
            cursor: pointer;
            position: absolute;
            top: 0;
            right: 0;
            z-index: 999;
        }

        .echartsBox {
            width: 100%;
            height: 100%;
        }
    }
}
</style>
相关推荐
长风清留扬5 分钟前
小程序毕业设计-音乐播放器+源码(可播放)下载即用
javascript·小程序·毕业设计·课程设计·毕设·音乐播放器
m0_7482478019 分钟前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
ZJ_.1 小时前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
joan_851 小时前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
还是大剑师兰特2 小时前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
Watermelo6172 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
一个处女座的程序猿O(∩_∩)O4 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
燃先生._.10 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖11 小时前
[react]searchParams转普通对象
开发语言·前端·javascript