forked from kaliatech/dygraphs-dynamiczooming-example
-
Notifications
You must be signed in to change notification settings - Fork 0
/
example1.html
232 lines (195 loc) · 8.92 KB
/
example1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example 1</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css" rel="stylesheet">
</head>
<style>
.graph-cont {
height: 300px;
width: 100%;
}
h4 {
margin-top: 1em;
}
footer {
margin-bottom: 20px;
}
.content img {
margin: 2em 0;
}
</style>
<body>
<a href="https://github.com/kaliatech/dygraphs-dynamiczooming-example"><img
style="position: absolute; top: 0; right: 0; border: 0;"
src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png" alt="Fork me on GitHub"></a>
<div class="container">
<div class="row">
<div class="col-md-12">
<h3>Using Dygraphs to load down-sampled data while zooming <small> - Example 1</small></h3>
<p>In the example below, new data is loaded every time the user zooms in on the graph. This provides an overall
view of the entire possible data set via the range selector below the graph, but allows interactive drill down
in to more detailed views of the data without having to load the entire data set at the
start. For very large data sets, loading all the data from the start is not practical.</p>
<p>The shaded areas above and below the solid line indicate min/max values whenever the data is being
down-sampled, and in this case, the solid line indicates the average value. As the user zooms in,
eventually there are no min/max indicators because the raw data values are being displayed.</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="graph-cont-1" class="graph-cont">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>Files</small></h3>
<dl>
<dt><a href="https://github.com/kaliatech/dygraphs-dynamiczooming-example/blob/master/example1.html">example1.html</a>
</dt>
<dd>This page. Contains the graph container's HTML and this documentation. No javascript.</dd>
</dl>
<dl>
<dt><a href="https://github.com/kaliatech/dygraphs-dynamiczooming-example/blob/master/j/JGS.Demo1Page.js">j/JGS.Demo1Page.js</a>
</dt>
<dd>This is the most relevant file. Contains all of the Dygraphs specific handling.
</dd>
</dl>
<dl>
<dt><a
href="https://github.com/kaliatech/dygraphs-dynamiczooming-example/blob/master/j/JGS.GraphDataProvider.js">j/JGS.GraphDataProvider.js</a>
</dt>
<dd>Responsible for getting range and detail data from the server/simulator and then aggregating,splicing, and
converting it for dygraphs.
</dd>
</dl>
<dl>
<dt><a
href="https://github.com/kaliatech/dygraphs-dynamiczooming-example/blob/master/j/JGS.ServerDataSimulator.js">j/JGS.ServerDataSimulator.js</a>
</dt>
<dd>Simulates a remote time series data service. Includes simulated delays.</dd>
</dl>
</div>
</div>
<div class="row">
<div class="col-md-12 content">
<h3>How it works</h3>
<p>
On page load, two data requests are sent to the server. One for the "range" data, and one for the
"detail" data. When zooming, a new request is made for more detailed data according to the new
zoom extents (or "dateWindow" in Dygraphs terminology.)
</p>
<p>
When the new detail data set is received, the code in GraphDataProvider.js splices it with the previously loaded
range data set to generate a single new "spliced data set". This spliced data set is what is given to
Dygraphs for drawing.</p>
<div>
<image src="i/datalayout.png" width="576" height="130" />
</div>
<p>This allows Dygraphs to draw and support the range selector control, but also draw the more
detailed data in the zoom window currently being viewed. The non-zoomed, more down-sampled, data can be seen
while dragging the range selector because the code does not initiate a new detail data load until
mouse-up.
</p>
<p>
The <strong>key to this approach is in understanding the different ways the zoom window (dateWindow) can be
changed in Dygraphs</strong> and the different results from each interaction. Interactions causing dateWindow to
be changed include:
</p>
<ul>
<li>A - Dragging on the range selector handles - Causes multiple zoomCallbacks while the position of the handle
is changing
</li>
<li>B - Dragging the range selector to move the dateWindow - No zoomCallback</li>
<li>C - Click-dragging on the graph to zoom - Causes a single zoomCallback</li>
<li>D - Click-dragging while holding Shift to pan - No zoomCallback, and not handled in examples 1 & 2. See <a href="example3.html">example 3</a>.
</li>
</ul>
<p>
To handle these interactions, the code installs a mouse-down and subsequent mouse-up listener on the
range selector. (It also installs corresponding touch event listeners so this works on tablets.) When
a mouse down occurs on the range selector, the code tracks it and ignores all subsequent zoomCallbacks. This
handles (A). We initiate the data load on mouse-up. This handles (A) and (B). If there was no mouse-down on
the range selector and we get a zoomCallback, then we assume (C), and so initiate an immediate data load. Handling (D)
requires hooking in to the drawCallback and is not shown in this example.
</p>
<p>That's basically it. The code, and comments in the code, shows the details.</p>
<p>Other examples:
<ul>
<li><a href="example2.html">example 2</a> - shows functionality for changing the overall range</li>
<li><a href="example3.html">example 3</a> - shows how to handle pan interaction</li>
</ul>
</p>
<h4>Third party libraries</h4>
<p>
I used jQuery because it simplifies the code and, honestly, I've probably started forgetting how to work with
DOM events correctly without it. I also used two other third party libraries that are not required, but
made this example a little better. Both can be easily removed.
</p>
<ul>
<li><a href="http://momentjs.com/">moment.js</a> - makes working with JavaScript dates so much better.</li>
<li><a href="http://fgnass.github.io/spin.js/">spin.js</a> - used to indicate when data is being loaded. A
simple "data loading..." message would also work. It's too bad <a
href="http://www.webmonkey.com/2013/04/its-the-end-of-the-blink-tag-as-we-know-it/">firefox removed the
blink tag</a>.
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>Credits</small></h3>
<p><a href="http://dygraphs.com">Dygraphs</a> is a well designed and robust library. Thanks to the <a
href="http://blog.dygraphs.com/2013/08/announcing-dygraphs-100.html">Dygraphs developers</a>
for releasing it with an MIT license and for continuing to supporting it.</p>
<p>Credit also to <a href="https://www.kcftech.com/">KCF Technologies</a> for giving me permission to
publish code showing the basic technique above. I flushed out the technique while doing work for
them on their next generation <a href="https://www.kcftech.com/smartdiagnostics/">Smart Diagnostics</a>
product, which uses a <em>much</em> more complicated variant of the example graph above. </p>
</div>
</div>
<footer>
<hr>
<div class="row">
<div class="col-md-12">
© 2013 JGS Technical LLC
</div>
</div>
</footer>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="j/lib/dygraph-1.0.combined.js"></script>
<script src="j/lib/spin-1.3.0.min.js"></script>
<script src="j/lib/moment-2.1.0.min.js"></script>
<script src="j/JGS.ServerDataSimulator.js"></script>
<script src="j/JGS.GraphDataProvider.js"></script>
<script src="j/JGS.Demo1Page.js"></script>
<script>
$(document).ready(function () {
var pageCfg = {
$graphCont: $("#graph-cont-1")
};
var demo1Page = new JGS.Demo1Page(pageCfg);
demo1Page.init();
});
</script>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-8344371-7");
pageTracker._trackPageview();
} catch(err) {}
</script>
</body>
</html>