使用ResizeObserver监听ECharts图表DOM尺寸变化

文章介绍了ECharts的渲染结束事件(rendered)和完成事件(finished),以及如何使用ResizeObserver监听DOM尺寸变化,通过节流函数优化性能,防止频繁调用ECharts的resize方法。同时提到了对动画完成事件的处理策略,以避免多次触发监听器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

监测DOM元素尺寸大小变化|Vue

如何优雅监听容器高度变化

上面两个链接地址,介绍了 ResizeObserver 以及它对浏览器的兼容扩展:resize-observer-polyfill element-resize-detector vue-resize-observer

ResizeObserver API已经被普遍兼容,这些扩展将不再被需要

ResizeObserver的用法示例:

textarea {
    width: 200px; height: 100px;
    border-image: linear-gradient(deepskyblue, deeppink) 1;    
}
<textarea id="roElement">本文地址:https://www.zhangxinxu.com/wordpress/?p=9295
作者:zhangxinxu</textarea>
var eleRo = document.getElementById("roElement");
var objResizeObserver = new ResizeObserver(function (entries) {
  var entry = entries[0];
  var cr = entry.contentRect;
  var target = entry.target;
  var angle = cr.width - 200 + (cr.height - 100);
  target.style.borderImageSource =
    "linear-gradient(" + angle + "deg, deepskyblue, deeppink)";
});
// 观察文本域元素
objResizeObserver.observe(eleRo);

ECharts

ECharts图表中,给出了 renderedfinished 两个函数:

events. rendered

Event

渲染结束事件。注意 rendered 事件并不代表渲染动画(参见 animation 相关配置)或者渐进渲染(参见 progressive 相关配置)停止,只代表本帧的渲染结束。

例如:

var snapshotImage = new Image();
document.body.append(snapshotImage);
chart.on('rendered', function () {
    snapshotImage.src = chart.getDataURL();
});
events. finished

Event

渲染完成事件。当渲染动画(参见 animation 相关配置)或者渐进渲染(参见 progressive 相关配置)停止时触发。

var snapshotImage = new Image();
document.body.append(snapshotImage);
chart.on('finished', function () {
    snapshotImage.src = chart.getDataURL();
});

注意:建议在调用 setOption 前注册相关事件,否则在动画被禁用时,注册的事件回调可能因时序问题而不被执行。

var option = {
    // ...
    animation: false
    // ...
};
chart.on('finished', function () {
    // ...  
});
chart.setOption(option);

代码示例

等待echarts图表首次的入场动画加载完毕,使用 resizeObserver 对dom的尺寸监听;

为提高性能,通过节流函数调用echarts的 resize 调整echarts尺寸。

完整代码

<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="roElement" style="width: 100%; height: 400px"></div>

<!-- 引入 Apache ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>

<script>
    // 指定图表的配置项和数据
    const option = {
        xAxis: {
            data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
            boundaryGap: false,
            axisTick: {show: false},
        },
        grid: {left: 10, right: 10, bottom: 20, top: 30, containLabel: true},
        tooltip: {
            trigger: "axis",
            axisPointer: {type: "cross"},
            padding: [5, 10],
        },
        yAxis: {axisTick: {show: false}},
        legend: {data: ["expected", "actual"]},
        series: [
            {
                name: "expected",
                lineStyle: {
                    color: "#FF005A",
                    lineStyle: {color: "#FF005A", width: 2},
                },
                smooth: true,
                type: "line",
                data: [100, 120, 161, 134, 105, 160, 165],
                animationDuration: 2800, // 入场动画时长
                animationEasing: "cubicInOut", // 入场动画缓动
            },
            {
                name: "actual",
                smooth: true,
                type: "line",
                lineStyle: {
                    color: "#3888fa",
                    lineStyle: {color: "#3888fa", width: 2},
                    areaStyle: {color: "#f3f8ff"},
                },
                data: [120, 82, 91, 154, 162, 140, 145],
                animationDuration: 2800,
                animationEasing: "quadraticOut",
            },
        ],
    };
    const eleRo = document.getElementById("roElement"); // 获取dom元素
    const chart = echarts.init(eleRo, "dark"); // 基于准备好的dom,初始化echarts实例

    /**
     * ResizeObserver 构造函数创建一个新的 ResizeObserver 对象
     * 它可以用于监听 Element 内容盒或边框盒或者 SVGElement 边界尺寸的大小
     * */
    const objResizeObserver = new ResizeObserver(function (entries) {
        console.log("观察者函数被执行");
        throttledFun();
    });
    /**
     * @desc 函数节流
     * @param fn 函数
     * @param wait 延迟执行毫秒数
     */
    const throttle = function (fn, wait) {
        let timer = null;
        return function () {
            let context = this;
            let args = arguments;
            if (!timer) {
                // 当延迟时间结束后,执行函数
                timer = setTimeout(() => {
                    timer = null;
                    fn.apply(context, args);
                }, wait);
            }
        };
    };
    const throttledFun = throttle(function () {
        console.log("节流函数被触发");
        chart.resize(); // 调整echarts尺寸
    }, 1000); // 频繁调用resize函数会影响性能,所以通过节流函数触发resize

    // 在调用 setOption 前注册相关事件,否则在动画被禁用时,注册的事件回调可能因时序问题而不被执行。
    /**
     * 监听 echarts 的 finished 事件 渲染完成事件
     * 当渲染动画或者渐进渲染停止时触发
     * finished 事件在每次重新渲染图表后都会触发,如果有多次重新渲染,就会出现监听器函数被多次调用的情况
     * (包括首次加载的动画,鼠标悬停移动时触发的图表动画)
     * 为了避免这种情况,给出以下两种处理方案:
     * 方案一:使用一个标记来跟踪触发次数,确保只添加一次 ResizeObserver
     * 方案二:利用异步编程promise,把完成后的动作放到异步队列中(推荐)
     * */

    //#region 方案一:使用一个标记来跟踪触发次数,确保只添加一次 ResizeObserver
    // let resizeObserverAdded = false;
    // chart.on("finished", function () {
    //     console.log("finished函数被触发");
    //     if (!resizeObserverAdded) {
    //         console.log("finished函数内的,绑定监听被触发(仅一次)");
    //         // 观察文本域元素
    //         objResizeObserver.observe(eleRo);
    //         // 对浏览器窗口尺寸变化进行监听(在这里使用并不合适)
    //         // window.addEventListener('resize', throttledFun);
    //     }
    //     resizeObserverAdded = true;
    // });
    //
    // chart.setOption(option); // 使用刚指定的配置项和数据显示图表。
    //#endregion

    //#region 方案二:利用异步编程promise,把完成后的动作放到异步队列中(推荐)
    const chartsPromise = new Promise((resolve) => {
        chart.on("finished", () => {
            console.log("finished函数被触发");
            resolve(); // 把执行结果抛出去
        });

        chart.setOption(option);
    });

    chartsPromise.then(() => {
        objResizeObserver.observe(eleRo); // 确保该函数(绑定监听)只触发一次
    });
    //#endregion
</script>

解决 finished 事件频繁触发的问题,方案二:利用异步编程promise把完成后的动作放到异步队列中
参考:echarts渲染完成事件finished/rendered

效果展示

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值