use intersectionObserver when available
Costa Tsaousis (ktsaou) committed
Jan 14, 2018 at 00:56 UTC
c13350c4c5dfa3f095d6133e3fc2c8d3e9c3bab9
1 file changed
+177
-72
web/dashboard.js
+177
-72
@@ -287,7 +287,7 @@ var NETDATA = window.NETDATA || {};
287
288
pause: false, // when enabled we don't auto-refresh the charts
289
290
- targets: null, // an array of all the state objects that are
290
+ targets: [], // an array of all the state objects that are
291
// currently active (independently of their
292
// viewport visibility)
293
@@ -374,7 +374,7 @@ var NETDATA = window.NETDATA || {};
374
375
pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
376
377
- update_only_visible: true, // enable or disable visibility management
377
+ update_only_visible: true, // enable or disable visibility management / used for printing
378
379
parallel_refresher: (isSlowDevice() === false), // enable parallel refresh of charts
380
@@ -759,35 +759,27 @@ var NETDATA = window.NETDATA || {};
759
760
NETDATA.onscroll_updater_timeout_id = undefined;
761
NETDATA.onscroll_updater = function() {
762
- if(NETDATA.options.targets === null) return;
763
-
764
- //var start = Date.now();
765
-
766
- // console.log('onscroll_updater() begin');
767
-
762
NETDATA.globalSelectionSync.stop();
763
764
+ if(NETDATA.options.abort_ajax_on_scroll === true)
765
+ NETDATA.abort_all_refreshes();
766
+
767
// when the user scrolls he sees that we have
768
// hidden all the not-visible charts
769
// using this little function we try to switch
770
// the charts back to visible quickly
771
775
- if(NETDATA.options.abort_ajax_on_scroll === true)
776
- NETDATA.abort_all_refreshes();
777
-
778
- if(NETDATA.options.current.parallel_refresher === false) {
779
- var targets = NETDATA.options.targets;
780
- var len = targets.length;
772
+ if(NETDATA.intersectionObserver.enabled() === false) {
773
+ if (NETDATA.options.current.parallel_refresher === false) {
774
+ var targets = NETDATA.options.targets;
775
+ var len = targets.length;
776
782
- while (len--)
783
- if(targets[len].running === false)
784
- targets[len].isVisible();
777
+ while (len--)
778
+ if (targets[len].running === false)
779
+ targets[len].isVisible();
780
+ }
781
}
782
787
- //var end = Date.now();
788
- //var dt = end - start;
789
- // console.log('count = ' + targets.length + ', average = ' + Math.round(dt / targets.length).toString() + ', dt = ' + dt);
790
-
783
NETDATA.onscroll_end_delay();
784
};
785
@@ -2207,10 +2199,11 @@ var NETDATA = window.NETDATA || {};
2199
this.last_t = 0;
2200
2201
// find all slaves
2202
+ var targets = NETDATA.intersectionObserver.targets();
2203
this.slaves = [];
2211
- var len = NETDATA.options.targets.length;
2204
+ var len = targets.length;
2205
while(len--) {
2213
- var st = NETDATA.options.targets[len];
2206
+ var st = targets[len];
2207
if (this.state !== st && st.globalSelectionSyncIsEligible() === true)
2208
this.slaves.push(st);
2209
}
@@ -2285,6 +2278,109 @@ var NETDATA = window.NETDATA || {};
2278
}
2279
};
2280
2281
+ NETDATA.intersectionObserver = {
2282
+ observer: null,
2283
+ visible_targets: [],
2284
+
2285
+ options: {
2286
+ root: null,
2287
+ rootMargin: "0px",
2288
+ threshold: null
2289
+ },
2290
+
2291
+ enabled: function() {
2292
+ return this.observer !== null;
2293
+ },
2294
+
2295
+ globalReset: function() {
2296
+ if(this.observer !== null) {
2297
+ this.visible_targets = [];
2298
+ this.observer.disconnect();
2299
+ this.init();
2300
+ }
2301
+ },
2302
+
2303
+ targets: function() {
2304
+ if(this.enabled() === true && this.visible_targets.length > 0)
2305
+ return this.visible_targets;
2306
+ else
2307
+ return NETDATA.options.targets;
2308
+ },
2309
+
2310
+ switchChartVisibility: function() {
2311
+ var old = this.__visibilityRatioOld;
2312
+
2313
+ if(old !== this.__visibilityRatio) {
2314
+ if (old === 0 && this.__visibilityRatio > 0)
2315
+ this.unhideChart();
2316
+ else if (old > 0 && this.__visibilityRatio === 0)
2317
+ this.hideChart();
2318
+
2319
+ this.__visibilityRatioOld = this.__visibilityRatio;
2320
+ }
2321
+ },
2322
+
2323
+ handler: function(entries, observer) {
2324
+ entries.forEach(function(entry) {
2325
+ var state = NETDATA.chartState(entry.target);
2326
+
2327
+ if(entry.intersectionRatio > 0) {
2328
+ var idx = NETDATA.intersectionObserver.visible_targets.indexOf(state);
2329
+ if(idx == -1)
2330
+ NETDATA.intersectionObserver.visible_targets.push(state);
2331
+ else if(state.__visibilityRatio === 0)
2332
+ state.log("was not visible until now, but was already in visible_targets");
2333
+ }
2334
+ else {
2335
+ var idx = NETDATA.intersectionObserver.visible_targets.indexOf(state);
2336
+ if(idx > -1)
2337
+ NETDATA.intersectionObserver.visible_targets.splice(idx, 1);
2338
+ else if(state.__visibilityRatio > 0)
2339
+ state.log("was visible, but not found in visible_targets");
2340
+ }
2341
+
2342
+ state.__visibilityRatio = entry.intersectionRatio;
2343
+
2344
+ if(NETDATA.options.current.async_on_scroll === false) {
2345
+ if(window.requestIdleCallback)
2346
+ window.requestIdleCallback(function() {
2347
+ NETDATA.intersectionObserver.switchChartVisibility.call(state);
2348
+ }, {timeout: 100});
2349
+ else
2350
+ NETDATA.intersectionObserver.switchChartVisibility.call(state);
2351
+ }
2352
+ });
2353
+ },
2354
+
2355
+ observe: function(state) {
2356
+ if(this.enabled() === true) {
2357
+ state.__visibilityRatioOld = 0;
2358
+ state.__visibilityRatio = 0;
2359
+ this.observer.observe(state.element);
2360
+
2361
+ state.isVisible = function() {
2362
+ if(NETDATA.options.current.update_only_visible === false)
2363
+ return true;
2364
+
2365
+ NETDATA.intersectionObserver.switchChartVisibility.call(this);
2366
+
2367
+ return this.__visibilityRatio > 0;
2368
+ }
2369
+ }
2370
+ },
2371
+
2372
+ init: function() {
2373
+ try {
2374
+ this.observer = new IntersectionObserver(this.handler, this.options);
2375
+ }
2376
+ catch(e) {
2377
+ console.log("IntersectionObserver is not supported on this browser");
2378
+ this.observer = null;
2379
+ }
2380
+ }
2381
+ };
2382
+ NETDATA.intersectionObserver.init();
2383
+
2384
// ----------------------------------------------------------------------------------------------------------------
2385
// Our state object, where all per-chart values are stored
2386
@@ -2715,86 +2811,86 @@ var NETDATA = window.NETDATA || {};
2811
};
2812
2813
// hide the chart, when it is not visible - called from isVisible()
2718
- var hideChart = function() {
2814
+ this.hideChart = function() {
2815
// hide it, if it is not already hidden
2816
if(isHidden() === true) return;
2817
2722
- if(that.chart_created === true) {
2818
+ if(this.chart_created === true) {
2819
if(NETDATA.options.current.show_help === true) {
2724
- if(that.element_legend_childs.toolbox !== null) {
2725
- if(that.debug === true)
2726
- that.log('hideChart(): hidding legend popovers');
2727
-
2728
- $(that.element_legend_childs.toolbox_left).popover('hide');
2729
- $(that.element_legend_childs.toolbox_reset).popover('hide');
2730
- $(that.element_legend_childs.toolbox_right).popover('hide');
2731
- $(that.element_legend_childs.toolbox_zoomin).popover('hide');
2732
- $(that.element_legend_childs.toolbox_zoomout).popover('hide');
2820
+ if(this.element_legend_childs.toolbox !== null) {
2821
+ if(this.debug === true)
2822
+ this.log('hideChart(): hidding legend popovers');
2823
+
2824
+ $(this.element_legend_childs.toolbox_left).popover('hide');
2825
+ $(this.element_legend_childs.toolbox_reset).popover('hide');
2826
+ $(this.element_legend_childs.toolbox_right).popover('hide');
2827
+ $(this.element_legend_childs.toolbox_zoomin).popover('hide');
2828
+ $(this.element_legend_childs.toolbox_zoomout).popover('hide');
2829
}
2830
2735
- if(that.element_legend_childs.resize_handler !== null)
2736
- $(that.element_legend_childs.resize_handler).popover('hide');
2831
+ if(this.element_legend_childs.resize_handler !== null)
2832
+ $(this.element_legend_childs.resize_handler).popover('hide');
2833
2738
- if(that.element_legend_childs.content !== null)
2739
- $(that.element_legend_childs.content).popover('hide');
2834
+ if(this.element_legend_childs.content !== null)
2835
+ $(this.element_legend_childs.content).popover('hide');
2836
}
2837
2838
if(NETDATA.options.current.destroy_on_hide === true) {
2743
- if(that.debug === true)
2744
- that.log('hideChart(): initializing chart');
2839
+ if(this.debug === true)
2840
+ this.log('hideChart(): initializing chart');
2841
2842
// we should destroy it
2843
init('force');
2844
}
2845
else {
2750
- if(that.debug === true)
2751
- that.log('hideChart(): hiding chart');
2846
+ if(this.debug === true)
2847
+ this.log('hideChart(): hiding chart');
2848
2849
showRendering();
2754
- that.element_chart.style.display = 'none';
2755
- that.element.style.willChange = 'auto';
2756
- if(that.element_legend !== null) that.element_legend.style.display = 'none';
2757
- if(that.element_legend_childs.toolbox !== null) that.element_legend_childs.toolbox.style.display = 'none';
2758
- if(that.element_legend_childs.resize_handler !== null) that.element_legend_childs.resize_handler.style.display = 'none';
2850
+ this.element_chart.style.display = 'none';
2851
+ this.element.style.willChange = 'auto';
2852
+ if(this.element_legend !== null) this.element_legend.style.display = 'none';
2853
+ if(this.element_legend_childs.toolbox !== null) this.element_legend_childs.toolbox.style.display = 'none';
2854
+ if(this.element_legend_childs.resize_handler !== null) this.element_legend_childs.resize_handler.style.display = 'none';
2855
2760
- that.tm.last_hidden = Date.now();
2856
+ this.tm.last_hidden = Date.now();
2857
2858
// de-allocate data
2859
// This works, but I not sure there are no corner cases somewhere
2860
// so it is commented - if the user has memory issues he can
2861
// set Destroy on Hide for all charts
2766
- // that.data = null;
2862
+ // this.data = null;
2863
}
2864
}
2865
2770
- that.tmp.___chartIsHidden___ = true;
2866
+ this.tmp.___chartIsHidden___ = true;
2867
};
2868
2869
// unhide the chart, when it is visible - called from isVisible()
2774
- var unhideChart = function() {
2870
+ this.unhideChart = function() {
2871
if(isHidden() === false) return;
2872
2777
- that.tmp.___chartIsHidden___ = undefined;
2778
- that.updates_since_last_unhide = 0;
2873
+ this.tmp.___chartIsHidden___ = undefined;
2874
+ this.updates_since_last_unhide = 0;
2875
2780
- if(that.chart_created === false) {
2781
- if(that.debug === true)
2782
- that.log('unhideChart(): initializing chart');
2876
+ if(this.chart_created === false) {
2877
+ if(this.debug === true)
2878
+ this.log('unhideChart(): initializing chart');
2879
2880
// we need to re-initialize it, to show our background
2881
// logo in bootstrap tabs, until the chart loads
2882
init('force');
2883
}
2884
else {
2789
- if(that.debug === true)
2790
- that.log('unhideChart(): unhiding chart');
2791
-
2792
- that.element.style.willChange = 'transform';
2793
- that.tm.last_unhidden = Date.now();
2794
- that.element_chart.style.display = '';
2795
- if(that.element_legend !== null) that.element_legend.style.display = '';
2796
- if(that.element_legend_childs.toolbox !== null) that.element_legend_childs.toolbox.style.display = '';
2797
- if(that.element_legend_childs.resize_handler !== null) that.element_legend_childs.resize_handler.style.display = '';
2885
+ if(this.debug === true)
2886
+ this.log('unhideChart(): unhiding chart');
2887
+
2888
+ this.element.style.willChange = 'transform';
2889
+ this.tm.last_unhidden = Date.now();
2890
+ this.element_chart.style.display = '';
2891
+ if(this.element_legend !== null) this.element_legend.style.display = '';
2892
+ if(this.element_legend_childs.toolbox !== null) this.element_legend_childs.toolbox.style.display = '';
2893
+ if(this.element_legend_childs.resize_handler !== null) this.element_legend_childs.resize_handler.style.display = '';
2894
resizeChart();
2895
hideMessage();
2896
}
@@ -2804,6 +2900,9 @@ var NETDATA = window.NETDATA || {};
2900
if(that.debug === true)
2901
that.log('canBeRendered() called');
2902
2903
+ if(NETDATA.options.current.update_only_visible === false)
2904
+ return true;
2905
+
2906
var ret = (
2907
(
2908
NETDATA.options.page_is_visible === true ||
@@ -4719,8 +4818,8 @@ var NETDATA = window.NETDATA || {};
4818
|| typeof this.tmp.___isVisible___ === 'undefined'
4819
|| this.tm.last_visible_check <= NETDATA.options.last_page_scroll) {
4820
this.tmp.___isVisible___ = __isVisible();
4722
- if (this.tmp.___isVisible___ === true) unhideChart();
4723
- else hideChart();
4821
+ if (this.tmp.___isVisible___ === true) this.unhideChart();
4822
+ else this.hideChart();
4823
}
4824
4825
if(this.debug === true)
@@ -5164,6 +5263,8 @@ var NETDATA = window.NETDATA || {};
5263
// this is purely sequential charts refresher
5264
// it is meant to be autonomous
5265
NETDATA.chartRefresherNoParallel = function(index, callback) {
5266
+ var targets = NETDATA.intersectionObserver.targets();
5267
+
5268
if(NETDATA.options.debug.main_loop === true)
5269
console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
5270
@@ -5173,7 +5274,7 @@ var NETDATA = window.NETDATA || {};
5274
NETDATA.parseDom(callback);
5275
return;
5276
}
5176
- if(index >= NETDATA.options.targets.length) {
5277
+ if(index >= targets.length) {
5278
if(NETDATA.options.debug.main_loop === true)
5279
console.log('waiting to restart main loop...');
5280
@@ -5181,7 +5282,7 @@ var NETDATA = window.NETDATA || {};
5282
callback();
5283
}
5284
else {
5184
- var state = NETDATA.options.targets[index];
5285
+ var state = targets[index];
5286
5287
if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
5288
if(NETDATA.options.debug.main_loop === true)
@@ -5314,7 +5415,7 @@ var NETDATA = window.NETDATA || {};
5415
5416
if(NETDATA.globalSelectionSync.active() === false) {
5417
var parallel = [];
5317
- var targets = NETDATA.options.targets;
5418
+ var targets = NETDATA.intersectionObserver.targets();
5419
var len = targets.length;
5420
var state;
5421
while(len--) {
@@ -5374,12 +5475,15 @@ var NETDATA = window.NETDATA || {};
5475
if(NETDATA.options.debug.main_loop === true)
5476
console.log('DOM updated - there are ' + targets.length + ' charts on page.');
5477
5478
+ NETDATA.intersectionObserver.globalReset();
5479
NETDATA.options.targets = [];
5480
var len = targets.length;
5481
while(len--) {
5482
// the initialization will take care of sizing
5483
// and the "loading..." message
5382
- NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
5484
+ var state = NETDATA.chartState(targets[len]);
5485
+ NETDATA.options.targets.push(state);
5486
+ NETDATA.intersectionObserver.observe(state);
5487
}
5488
5489
if(NETDATA.globalChartUnderlay.isActive() === true)
@@ -5454,13 +5558,14 @@ var NETDATA = window.NETDATA || {};
5558
};
5559
5560
NETDATA.globalReset = function() {
5561
+ NETDATA.intersectionObserver.globalReset();
5562
NETDATA.globalSelectionSync.globalReset();
5563
NETDATA.globalPanAndZoom.globalReset();
5564
NETDATA.chartRegistry.globalReset();
5565
NETDATA.commonMin.globalReset();
5566
NETDATA.commonMax.globalReset();
5567
NETDATA.unitsConversion.globalReset();
5463
- NETDATA.options.targets = null;
5568
+ NETDATA.options.targets = [];
5569
NETDATA.parseDom();
5570
NETDATA.unpause();
5571
};