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自適應解決方案
解決思路:
- 偵聽div的變化,觸發resize
- 由於按鈕觸發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 分析過程
- 剝離框架,寫一個小程序,驗證方法的可行性(實際可行)
- 添加樣式,依舊可行
- 添加打印,對比resize起作用前后的變化。
- div容器的寬和高
- mycharts的寬和高(echarts對象的方法獲取)
- 在resize1起作用前,上面兩者是不一致的
- 回到框架代碼,發現觸發事件的時候,一直處於一致狀態。
- 通過我的好眼力,發現resize事件處理領先於div的寬高調整
- 進一步發下,這個按鈕綁定了其他事件,導致了先resize,然后再調整寬和高。
- 在之前綁定的事件的回調函數中,觸發新的事件,保證先后順序。
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 遇到問題解決方式
- 懷疑開源組件的問題
這個可以將開源組件剝離當前框架,寫個小程序直接驗證。直接來個demo,這個有時候還是不習慣,或者有點反應慢。
- 遇到問題的搜尋順序
- 搜索,要是有明確的答案直接用
- 官方論壇
- 再搜索
- 控制變量法
不確定問題原因的可以驗證單個因素的正確性,然后再組合驗證。發現是不是組合了才導致問題發生了。
