Skip to content

Commit e451c40

Browse files
author
travis@localhost
committed
Release 0.2.0
1 parent 883c6aa commit e451c40

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-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.2.0",
7+
"main": "./dist/chartjs-plugin-deferred.js"
8+
}

dist/chartjs-plugin-deferred.js

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

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)