Skip to content

Commit 2908c52

Browse files
author
travis@localhost
committed
Release 0.3.0
1 parent 89d4c6b commit 2908c52

File tree

3 files changed

+242
-0
lines changed

3 files changed

+242
-0
lines changed

bower.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "chartjs-plugin-deferred",
3+
"description": "Chart.js plugin to defer initial chart updates",
4+
"homepage": "http://www.chartjs.org",
5+
"license": "MIT",
6+
"version": "0.3.0",
7+
"main": "./dist/chartjs-plugin-deferred.js"
8+
}

dist/chartjs-plugin-deferred.js

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

dist/chartjs-plugin-deferred.min.js

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)