|
| 1 | +/*! |
| 2 | + * @license |
| 3 | + * chartjs-plugin-deferred v1.0.2 |
| 4 | + * https://chartjs-plugin-deferred.netlify.app |
| 5 | + * (c) 2016-2021 chartjs-plugin-deferred contributors |
| 6 | + * Released under the MIT license |
| 7 | + */ |
| 8 | +(function (global, factory) { |
| 9 | + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js')) : |
| 10 | + typeof define === 'function' && define.amd ? define(['chart.js'], factory) : |
| 11 | + (factory(global.Chart)); |
| 12 | +}(this, (function (Chart) { 'use strict'; |
| 13 | + |
| 14 | +Chart = Chart && Chart.hasOwnProperty('default') ? Chart['default'] : Chart; |
| 15 | + |
| 16 | +'use strict'; |
| 17 | + |
| 18 | +var helpers = Chart.helpers; |
| 19 | +var STUB_KEY = '$chartjs_deferred'; |
| 20 | +var MODEL_KEY = '$deferred'; |
| 21 | + |
| 22 | +/** |
| 23 | + * Plugin based on discussion from Chart.js issue #2745. |
| 24 | + * @see https://github.com/chartjs/Chart.js/issues/2745 |
| 25 | + */ |
| 26 | +Chart.defaults.global.plugins.deferred = { |
| 27 | + xOffset: 0, |
| 28 | + yOffset: 0, |
| 29 | + delay: 0 |
| 30 | +}; |
| 31 | + |
| 32 | +function defer(fn, delay) { |
| 33 | + if (delay) { |
| 34 | + window.setTimeout(fn, delay); |
| 35 | + } else { |
| 36 | + helpers.requestAnimFrame.call(window, fn); |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +function computeOffset(value, base) { |
| 41 | + var number = parseInt(value, 10); |
| 42 | + if (isNaN(number)) { |
| 43 | + return 0; |
| 44 | + } else if (typeof value === 'string' && value.indexOf('%') !== -1) { |
| 45 | + return number / 100 * base; |
| 46 | + } |
| 47 | + return number; |
| 48 | +} |
| 49 | + |
| 50 | +function chartInViewport(chart) { |
| 51 | + var options = chart[MODEL_KEY].options; |
| 52 | + var canvas = chart.chart.canvas; |
| 53 | + |
| 54 | + // http://stackoverflow.com/a/21696585 |
| 55 | + if (!canvas || canvas.offsetParent === null) { |
| 56 | + return false; |
| 57 | + } |
| 58 | + |
| 59 | + var rect = canvas.getBoundingClientRect(); |
| 60 | + var dy = computeOffset(options.yOffset || 0, rect.height); |
| 61 | + var dx = computeOffset(options.xOffset || 0, rect.width); |
| 62 | + |
| 63 | + return rect.right - dx >= 0 |
| 64 | + && rect.bottom - dy >= 0 |
| 65 | + && rect.left + dx <= window.innerWidth |
| 66 | + && rect.top + dy <= window.innerHeight; |
| 67 | +} |
| 68 | + |
| 69 | +function onScroll(event) { |
| 70 | + var node = event.target; |
| 71 | + var stub = node[STUB_KEY]; |
| 72 | + if (stub.ticking) { |
| 73 | + return; |
| 74 | + } |
| 75 | + |
| 76 | + stub.ticking = true; |
| 77 | + defer(function() { |
| 78 | + var charts = stub.charts.slice(); |
| 79 | + var ilen = charts.length; |
| 80 | + var chart, i; |
| 81 | + |
| 82 | + for (i = 0; i < ilen; ++i) { |
| 83 | + chart = charts[i]; |
| 84 | + if (chartInViewport(chart)) { |
| 85 | + unwatch(chart); // eslint-disable-line |
| 86 | + chart[MODEL_KEY].appeared = true; |
| 87 | + chart.update(); |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + stub.ticking = false; |
| 92 | + }); |
| 93 | +} |
| 94 | + |
| 95 | +function isScrollable(node) { |
| 96 | + var type = node.nodeType; |
| 97 | + if (type === Node.ELEMENT_NODE) { |
| 98 | + var overflowX = helpers.getStyle(node, 'overflow-x'); |
| 99 | + var overflowY = helpers.getStyle(node, 'overflow-y'); |
| 100 | + return overflowX === 'auto' || overflowX === 'scroll' |
| 101 | + || overflowY === 'auto' || overflowY === 'scroll'; |
| 102 | + } |
| 103 | + |
| 104 | + return node.nodeType === Node.DOCUMENT_NODE; |
| 105 | +} |
| 106 | + |
| 107 | +function watch(chart) { |
| 108 | + var canvas = chart.chart.canvas; |
| 109 | + var parent = canvas.parentElement; |
| 110 | + var stub, charts; |
| 111 | + |
| 112 | + while (parent) { |
| 113 | + if (isScrollable(parent)) { |
| 114 | + stub = parent[STUB_KEY] || (parent[STUB_KEY] = {}); |
| 115 | + charts = stub.charts || (stub.charts = []); |
| 116 | + if (charts.length === 0) { |
| 117 | + helpers.addEvent(parent, 'scroll', onScroll); |
| 118 | + } |
| 119 | + |
| 120 | + charts.push(chart); |
| 121 | + chart[MODEL_KEY].elements.push(parent); |
| 122 | + } |
| 123 | + |
| 124 | + parent = parent.parentElement || parent.ownerDocument; |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +function unwatch(chart) { |
| 129 | + chart[MODEL_KEY].elements.forEach(function(element) { |
| 130 | + var charts = element[STUB_KEY].charts; |
| 131 | + charts.splice(charts.indexOf(chart), 1); |
| 132 | + if (!charts.length) { |
| 133 | + helpers.removeEvent(element, 'scroll', onScroll); |
| 134 | + delete element[STUB_KEY]; |
| 135 | + } |
| 136 | + }); |
| 137 | + |
| 138 | + chart[MODEL_KEY].elements = []; |
| 139 | +} |
| 140 | + |
| 141 | +Chart.plugins.register({ |
| 142 | + id: 'deferred', |
| 143 | + |
| 144 | + beforeInit: function(chart, options) { |
| 145 | + chart[MODEL_KEY] = { |
| 146 | + options: options, |
| 147 | + appeared: false, |
| 148 | + delayed: false, |
| 149 | + loaded: false, |
| 150 | + elements: [] |
| 151 | + }; |
| 152 | + |
| 153 | + watch(chart); |
| 154 | + }, |
| 155 | + |
| 156 | + beforeDatasetsUpdate: function(chart, options) { |
| 157 | + var model = chart[MODEL_KEY]; |
| 158 | + if (!model.loaded) { |
| 159 | + if (!model.appeared && !chartInViewport(chart)) { |
| 160 | + // cancel the datasets update |
| 161 | + return false; |
| 162 | + } |
| 163 | + |
| 164 | + model.appeared = true; |
| 165 | + model.loaded = true; |
| 166 | + unwatch(chart); |
| 167 | + |
| 168 | + if (options.delay > 0) { |
| 169 | + model.delayed = true; |
| 170 | + defer(function() { |
| 171 | + // Ensure the chart instance is still alive. It may have been destroyed |
| 172 | + // during a delay and calling `chart.update()` will fail. The most common |
| 173 | + // reason for such scenario is user navigation. |
| 174 | + // https://github.com/chartjs/chartjs-plugin-deferred/pull/14 |
| 175 | + if (chart.ctx) { |
| 176 | + model.delayed = false; |
| 177 | + chart.update(); |
| 178 | + } |
| 179 | + }, options.delay); |
| 180 | + |
| 181 | + return false; |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + if (model.delayed) { |
| 186 | + // in case of delayed update, ensure to block external requests, such |
| 187 | + // as interacting with the legend label, or direct calls to update() |
| 188 | + return false; |
| 189 | + } |
| 190 | + }, |
| 191 | + |
| 192 | + destroy: function(chart) { |
| 193 | + unwatch(chart); |
| 194 | + } |
| 195 | +}); |
| 196 | + |
| 197 | +}))); |
0 commit comments