echarts遇到resize失效問題


1、問題描述

在前端頁面中,echarts圖形在容器div發生變化時,能夠自適應調整大小。注意這里說的是容器div發生變化時,不是瀏覽器窗口發生變化的時候。實際場景是,在點擊菜單的按鈕,導致了echarts容器div發生了變化,但echarts沒有resize(),導致很丑。

2、解決過程

2.1 項目的現狀

在窗口發生變化的時候,觸發了resize事件,echarts容器發生變化,調用echarts的resize函數,做了自適應調整。代碼實現如下(普通場景木問題,部分場景會造成內存泄露,取決於你的框架代碼的實現):

 myChart.setOption(option);
 window.addEventListener("resize", function () {
 	 myChart.resize();
});

如果事件綁定在window上,這些變量生命周期就會隨着頁簽一直存在,一直在內存當中。如果不停的重新加載和刷新(子頁面),就會造成內存泄露。正確姿勢如下:

     //在初始化頁面的時候注冊一下
 function resizeEcharts(){
 	//$box.find('[data-toggle="echarts"]') 獲取echarts的容器
     $box.find('[data-toggle="echarts"]').each(function(){
         var element = $(this)[0];
         echarts. getInstanceByDom(element).resize();
     })
 }
 window.addEventListener("resize",resizeEcharts);

2.2 div自適應解決方案

解決思路:

  1. 偵聽div的變化,觸發resize
  2. 由於按鈕觸發echarts的容器div的變化,按鈕綁定觸發事件

2.2.1 div的變化,觸發resize

實現一

(function ($, window, undefined) {
    var elems = $([]),//空的數組
        jq_resize = $.resize = $.extend($.resize, {}),
        timeout_id,
        str_setTimeout = 'setTimeout',
        str_resize = 'resize',
        str_data = str_resize + '-special-event',
        str_delay = 'delay',
        str_throttle = 'throttleWindow';
    jq_resize[str_delay] = 250;
    jq_resize[str_throttle] = true;
    $.event.special[str_resize] = {
        setup: function () {
            if (!jq_resize[str_throttle] && this[str_setTimeout]) {
                return false;
            }
            var elem = $(this);
            elems = elems.add(elem);
            $.data(this, str_data, {
                w: elem.width(),
                h: elem.height()
            });
            if (elems.length === 1) {
                loopy();
            }
        },
        teardown: function () {
            if (!jq_resize[str_throttle] && this[str_setTimeout]) {
                return false;
            }
            var elem = $(this);
            elems = elems.not(elem);
            elem.removeData(str_data);
            if (!elems.length) {
                clearTimeout(timeout_id);
            }
        },
        add: function (handleObj) {
            if (!jq_resize[str_throttle] && this[str_setTimeout]) {
                return false;
            }
            var old_handler;
 
            function new_handler(e, w, h) {
                var elem = $(this),
                    data = $.data(this, str_data);
                data.w = w !== undefined ? w : elem.width();
                data.h = h !== undefined ? h : elem.height();
                old_handler.apply(this, arguments);
            }
 
            if ($.isFunction(handleObj)) {
                old_handler = handleObj;
                return new_handler;
            } else {
                old_handler = handleObj.handler;
                handleObj.handler = new_handler;
            }
        }
    };
 
    function loopy() {
        timeout_id = window[str_setTimeout](function () {
            elems.each(function () {
                var elem = $(this),
                    width = elem.width(),
                    height = elem.height(),
                    data = $.data(this, str_data);
                if (width !== data.w || height !== data.h) {
                    elem.trigger(str_resize, [data.w = width, data.h = height]);
					//錯誤代碼(直接在這邊)  《《《《《《《《《《《《《《代碼1》》》》》》》》》》》》》》
                }
            });
            loopy();
        }, jq_resize[str_delay]);
    }
})(jQuery, this);
//監聽div大小變化事件
$("div").resize(function(){
    console.log("觸發了....");
})

這么一大段代碼,看着有點稀里糊塗的,看着網上的文章,說偵聽div的變化,當div的變化,觸發resize事件。在本地實驗可以,的確可以偵聽到。如果直接在《代碼1》寫觸發代碼,會一直循環,還無法偵聽指定div的變化。對jquery的事件綁定,這部分知識欠缺,讀這段代碼,有點吃力。

實現二(未驗證):

2.2.2 觸發事件

觸發事件,思路比較簡單,實現也比較容易,但是容易掉坑里面。

2.2.2.1 探索過程

對定的元素綁定點擊事件,觸發resise事件,代碼如下:

//b被點擊的按鈕
var btn1=$("#hide-menu");
btn1.on("click",function(){
	var event= new Event("resize");
	window.dispatchEvent(event);
})

//echarts的容器
window.addEventListener("resize",function(){
	mychart.resize();
})

這個過程簡單吧,只是一個觸發事件過程和一個監聽事件過程。正常場景下,應該可以完美的解決這個問題。但是在項目框架中,就卡住了,雖然運行了,沒有達到預期的效果,一度讓我有點奔潰了。然后就陷入了一個怪坑,echarts的resize失效。網上找各種這個問題的解決方案,然並卵毫無作用。

說法一:“寬度100%,高度vh,就可以解決了”ECharts的resize失效原因以及使用方法

說法二:“$(“#vid”).css("height",$(vid).height);寬度一樣”

說法三:“特定版本的問題”

說法四:“樣式的影響”

......

2.2.2.2 分析過程
  1. 剝離框架,寫一個小程序,驗證方法的可行性(實際可行)
  2. 添加樣式,依舊可行
  3. 添加打印,對比resize起作用前后的變化。
    1. div容器的寬和高
    2. mycharts的寬和高(echarts對象的方法獲取)
    3. 在resize1起作用前,上面兩者是不一致的
  4. 回到框架代碼,發現觸發事件的時候,一直處於一致狀態。
    1. 通過我的好眼力,發現resize事件處理領先於div的寬高調整
  5. 進一步發下,這個按鈕綁定了其他事件,導致了先resize,然后再調整寬和高。
  6. 在之前綁定的事件的回調函數中,觸發新的事件,保證先后順序。
2.2.2.3 代碼實現過程

1、正確的實現的姿勢

var btn1=$("#hide-menu");
btn1.on("click",function(){
   //省略
       $navtab
       .stop()
       .animate(opts, 'fast',function(){
       	    var myEvent = new Event("echartDivChange");
		    window.dispatchEvent(myEvent);
       })
})
//echarts的容器
window.addEventListener("echartDivChange",function(){
	mychart.resize();
})

2、trigger 觸發事件,又掉入另外一個坑里面(錯誤示范)

var btn1=$("#hide-menu");
btn1.on("click",function(){
	//........
       $navtab
       .stop()
       .animate(opts, 'fast',function(){        	  
			 $(window).trigger("echartDivChange");//方式1	
 			//$(document).trigger("echartDivChange");//方式2
			//$("#123").trigger("echartDivChange");//方式3
       })
})
//echarts的容器(錯誤示范)
$("#mycharts").on("echartDivChange",function(){
	mychart.resize();
})

看框架源碼,有的事件是交叉觸發和捕獲,這種方式應該是錯誤的。在這種方式中無法捕獲,一定要選擇對應方式。 例如,$(window)需要用 $(window)去捕獲。這里面還有知識點,事件的冒泡機制,理解這個,對事件觸發和捕獲才能合理利用。

3、知識點提煉

  • resize事件
  • on
  • 自定義event觸發和捕獲
  • 冒泡機制
  • $.event.special(待學習)

4、總結

在定位這個問題過程中,還是花了很長時間的。在定位過程,掉進坑里面,無法爬出來。原因分為兩個方面,一:知識體系不夠完善;二:遇到坑的分析解決的方式有問題。

4.1 知識體系不夠完善

只能帶着積累,學習新的東西。

4.2 遇到問題解決方式

  1. 懷疑開源組件的問題

這個可以將開源組件剝離當前框架,寫個小程序直接驗證。直接來個demo,這個有時候還是不習慣,或者有點反應慢。

  1. 遇到問題的搜尋順序
  • 搜索,要是有明確的答案直接用
  • 官方論壇
  • 再搜索
  1. 控制變量法

不確定問題原因的可以驗證單個因素的正確性,然后再組合驗證。發現是不是組合了才導致問題發生了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM