Use ResizeObserver to monitor the DOM size change of ECharts chart

Monitor DOM element size changes | Vue

How to Elegantly Monitor Container Height Changes

The above two link addresses introduce ResizeObserverand its compatible extensions to browsers:resize-observer-polyfill element-resize-detector vue-resize-observer

But ResizeObserverthe API has been generally compatible, these extensions will no longer be needed

ResizeObserverUsage example for :

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

renderedIn the ECharts chart, two functions and are given finished:

events. rendered

Event

Render end event. Note that renderedthe event does not mean that the rendering animation (see animation related configuration) or progressive rendering (see progressive related configuration) stops, but only means that the rendering of this frame ends.

For example:

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

events. finished

Event

Render complete event. Triggered when rendering animation (see animation related configuration) or progressive rendering (see progressive related configuration) stops.

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

Note: It is recommended to register related events before calling setOption, otherwise, when the animation is disabled, the registered event callback may not be executed due to timing issues.

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

code example

Wait for the first entry animation of the echarts chart to be loaded, and use to resizeObservermonitor the size of the dom;

To improve performance, resizeadjust the size of echarts by calling echarts through the throttling function.

full code

<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="roElement" style="width: 100%; height: 400px"></div>
<!-- 引入 Apache ECharts -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/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 边界尺寸的大小
   *
   * 由于每次调用 ResizeObserver 回调函数时都会创建一个新的上下文环境
   * 因此在 resizeObserver 函数内部不能实现节流函数的共享变量
   * 即节流函数不能通过闭包实现,需要将共享变量timer定义在回调函数外部
   * */
  const objResizeObserver = new ResizeObserver(function (entries) {
      
      
    console.log("观察者函数被执行");
    throttle(function (params) {
      
      
      console.log("节流函数被触发");
      chart.resize(); // 调整echarts尺寸
    }, 1000)(); // 频繁调用resize函数会影响性能,所以通过节流函数触发resize
  });
  // 在调用 setOption 前注册相关事件,否则在动画被禁用时,注册的事件回调可能因时序问题而不被执行。
  /**
   * 监听 echarts 的 finished 事件 渲染完成事件
   * 当渲染动画或者渐进渲染停止时触发
   * finished 事件在每次重新渲染图表后都会触发,如果有多次重新渲染,就会出现监听器函数被多次调用的情况
   * (包括首次加载的动画,鼠标悬停移动时触发的图表动画)
   * 为了避免这种情况,可以使用一个标记来跟踪触发次数,确保只添加一次 ResizeObserver
   * */
  let resizeObserverAdded = false;
  chart.on("finished", function () {
      
      
    console.log("finished函数被触发");
    if (!resizeObserverAdded) {
      
      
      console.log("finished函数内的,绑定监听被触发(仅一次)");
      // 观察文本域元素
      objResizeObserver.observe(eleRo);
      // 对浏览器窗口尺寸变化进行监听(在这里使用并不合适)
      // window.addEventListener('resize', throttle(chart.resize, 1000,1));
    }
    resizeObserverAdded = true;
  });
  chart.setOption(option); // 使用刚指定的配置项和数据显示图表。
  /**
   * @desc 函数节流
   * @param fn 函数
   * @param wait 延迟执行毫秒数
   */
  let timer = null;
  const throttle = function (fn, wait) {
      
      
    return function () {
      
      
      let context = this;
      let args = arguments;
      if (!timer) {
      
      
        // 当延迟时间结束后,执行函数
        timer = setTimeout(() => {
      
      
          timer = null;
          fn.apply(context, args);
        }, wait);
      }
    };
  };
</script>

Code optimization

Reference: echarts rendering completion event finished/rendered

For finishedthe problem of frequent triggering of events, in addition to using tag tracking (declaring variables) above resizeObserverAdded, there is another solution:

Using asynchronous programming promise, put the completed action in the asynchronous queue

// let resizeObserverAdded = false;
// chart.on("finished", function () {
    
    
//   console.log("finished函数被触发");
//   if (!resizeObserverAdded) {
    
    
//     console.log("finished函数内的,绑定监听被触发(仅一次)");
//     // 观察文本域元素
//     objResizeObserver.observe(eleRo);
//     // 对浏览器窗口尺寸变化进行监听(在这里使用并不合适)
//     // window.addEventListener('resize', throttle(chart.resize, 1000,1));
//   }
//   resizeObserverAdded = true;
// });
// chart.setOption(option); // 使用刚指定的配置项和数据显示图表。
const chartsPromise = new Promise((resolve) => {
    
    
  chart.on("finished", () => {
    
    
    resolve(); // 把执行结果抛出去
  });
  chart.setOption(option);
});
chartsPromise.then(() => {
    
    
  objResizeObserver.observe(eleRo);
});

Show results

insert image description here

Guess you like

Origin blog.csdn.net/qq_51532249/article/details/131187482