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