| 1 | <!DOCTYPE html> |
| 2 | <html> |
| 3 | <head> |
| 4 | <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7; IE=EmulateIE9"> |
| 5 | <title>Plotters demo</title> |
| 6 | <!--[if IE]> |
| 7 | <script type="text/javascript" src="../excanvas.js"></script> |
| 8 | <![endif]--> |
| 9 | <script type="text/javascript" src="../dygraph-dev.js"></script> |
| 10 | |
| 11 | <script type="text/javascript" src="data.js"></script> |
| 12 | <style type="text/css"> |
| 13 | body { |
| 14 | max-width: 750px; |
| 15 | } |
| 16 | div.chart { |
| 17 | width: 640px; |
| 18 | height: 320px; |
| 19 | } |
| 20 | </style> |
| 21 | </head> |
| 22 | <body> |
| 23 | <p>This page demonstrates how to build custom plotters with dygraphs. |
| 24 | The <a href="http://dygraphs.com/options.html#plotter">plotter</a> option |
| 25 | allows you to write your own drawing logic. This can be used to achieve |
| 26 | powerful customization. View source to see how the examples work.</p> |
| 27 | |
| 28 | <h2>Bar Chart</h2> |
| 29 | <p>Here a specialized <a |
| 30 | href="http://dygraphs.com/options.html#plotter">plotter</a> is used to draw |
| 31 | a bar plot rather than a line plot:</p> |
| 32 | <div id="demodiv" class=chart></div> |
| 33 | |
| 34 | <h2>Candle Chart</h2> |
| 35 | <p>Here a specialized <a |
| 36 | href="http://dygraphs.com/options.html#plotter">plotter</a> is used to |
| 37 | combined four series into a unified "Candle" plot:</p> |
| 38 | <div id="candlechart" class=chart></div> |
| 39 | |
| 40 | <h2>Bar & Line Chart</h2> |
| 41 | <p>The <a href="http://dygraphs.com/options.html#plotter">plotter</a> |
| 42 | option may be set on a per-series basis to create mixed charts:</p> |
| 43 | <div id="barlinechart" class="chart"></div> |
| 44 | |
| 45 | <h2>Multi-column Bar Chart</h2> |
| 46 | <div id="multibar" class="chart"></div> |
| 47 | |
| 48 | <h2>Mixed Error Bars and Lines</h2> |
| 49 | <p>You can tweak the standard plotters list to achieve effects which would |
| 50 | be difficult otherwise, e.g. drawing series with only confidence intervals |
| 51 | and showing error bars only for some series.</p> |
| 52 | <div id="mixed-error" class="chart"></div> |
| 53 | |
| 54 | <script type="text/javascript"> |
| 55 | |
| 56 | // This function draws bars for a single series. See |
| 57 | // multiColumnBarPlotter below for a plotter which can draw multi-series |
| 58 | // bar charts. |
| 59 | function barChartPlotter(e) { |
| 60 | var ctx = e.drawingContext; |
| 61 | var points = e.points; |
| 62 | var y_bottom = e.dygraph.toDomYCoord(0); |
| 63 | |
| 64 | // The RGBColorParser class is provided by rgbcolor.js, which is |
| 65 | // packed in with dygraphs. |
| 66 | var color = new RGBColorParser(e.color); |
| 67 | color.r = Math.floor((255 + color.r) / 2); |
| 68 | color.g = Math.floor((255 + color.g) / 2); |
| 69 | color.b = Math.floor((255 + color.b) / 2); |
| 70 | ctx.fillStyle = color.toRGB(); |
| 71 | |
| 72 | // Find the minimum separation between x-values. |
| 73 | // This determines the bar width. |
| 74 | var min_sep = Infinity; |
| 75 | for (var i = 1; i < points.length; i++) { |
| 76 | var sep = points[i].canvasx - points[i - 1].canvasx; |
| 77 | if (sep < min_sep) min_sep = sep; |
| 78 | } |
| 79 | var bar_width = Math.floor(2.0 / 3 * min_sep); |
| 80 | |
| 81 | // Do the actual plotting. |
| 82 | for (var i = 0; i < points.length; i++) { |
| 83 | var p = points[i]; |
| 84 | var center_x = p.canvasx; |
| 85 | |
| 86 | ctx.fillRect(center_x - bar_width / 2, p.canvasy, |
| 87 | bar_width, y_bottom - p.canvasy); |
| 88 | |
| 89 | ctx.strokeRect(center_x - bar_width / 2, p.canvasy, |
| 90 | bar_width, y_bottom - p.canvasy); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | g = new Dygraph( |
| 95 | document.getElementById("demodiv"), |
| 96 | "Date,Widgets Sold\n" + |
| 97 | "2012/07/21,10\n" + |
| 98 | "2012/07/22,12\n" + |
| 99 | "2012/07/23,9\n" + |
| 100 | "2012/07/24,16\n" + |
| 101 | "2012/07/25,10\n", |
| 102 | { |
| 103 | legend: 'always', |
| 104 | title: 'Daily Widget Sales', |
| 105 | includeZero: true, |
| 106 | dateWindow: [ Date.parse("2012/07/20"), Date.parse("2012/07/26") ], |
| 107 | animatedZooms: true, |
| 108 | drawXGrid: false, |
| 109 | plotter: barChartPlotter |
| 110 | } |
| 111 | ); |
| 112 | |
| 113 | // The Candle chart plotter is adapted from code written by |
| 114 | // Zhenlei Cai (jpenguin@gmail.com) |
| 115 | // https://github.com/danvk/dygraphs/pull/141/files |
| 116 | |
| 117 | var BAR_WIDTH = 8; |
| 118 | function candlePlotter(e) { |
| 119 | // This is the officially endorsed way to plot all the series at once. |
| 120 | if (e.seriesIndex !== 0) return; |
| 121 | |
| 122 | var setCount = e.seriesCount; |
| 123 | if (setCount != 4) { |
| 124 | throw "Exactly 4 prices each point must be provided for candle chart (open close high low)"; |
| 125 | } |
| 126 | |
| 127 | var prices = []; |
| 128 | var price; |
| 129 | var sets = e.allSeriesPoints; |
| 130 | for (var p = 0 ; p < sets[0].length; p++) { |
| 131 | price = { |
| 132 | open : sets[0][p].yval, |
| 133 | close : sets[1][p].yval, |
| 134 | high : sets[2][p].yval, |
| 135 | low : sets[3][p].yval, |
| 136 | openY : sets[0][p].y, |
| 137 | closeY : sets[1][p].y, |
| 138 | highY : sets[2][p].y, |
| 139 | lowY : sets[3][p].y |
| 140 | }; |
| 141 | prices.push(price); |
| 142 | } |
| 143 | |
| 144 | var area = e.plotArea; |
| 145 | var ctx = e.drawingContext; |
| 146 | ctx.strokeStyle = '#202020'; |
| 147 | ctx.lineWidth = 0.6; |
| 148 | |
| 149 | for (p = 0 ; p < prices.length; p++) { |
| 150 | ctx.beginPath(); |
| 151 | |
| 152 | price = prices[p]; |
| 153 | var topY = area.h * price.highY + area.y; |
| 154 | var bottomY = area.h * price.lowY + area.y; |
| 155 | var centerX = area.x + sets[0][p].x * area.w; |
| 156 | ctx.moveTo(centerX, topY); |
| 157 | ctx.lineTo(centerX, bottomY); |
| 158 | ctx.closePath(); |
| 159 | ctx.stroke(); |
| 160 | var bodyY; |
| 161 | if (price.open > price.close) { |
| 162 | ctx.fillStyle ='rgba(244,44,44,1.0)'; |
| 163 | bodyY = area.h * price.openY + area.y; |
| 164 | } |
| 165 | else { |
| 166 | ctx.fillStyle ='rgba(44,244,44,1.0)'; |
| 167 | bodyY = area.h * price.closeY + area.y; |
| 168 | } |
| 169 | var bodyHeight = area.h * Math.abs(price.openY - price.closeY); |
| 170 | ctx.fillRect(centerX - BAR_WIDTH / 2, bodyY, BAR_WIDTH, bodyHeight); |
| 171 | } |
| 172 | |
| 173 | } |
| 174 | |
| 175 | var candleData = "Date,Open,Close,High,Low\n" + |
| 176 | "2011-12-06,392.54,390.95,394.63,389.38\n" + |
| 177 | "2011-12-07,389.93,389.09,390.94,386.76\n" + |
| 178 | "2011-12-08,391.45,390.66,395.50,390.23\n" + |
| 179 | "2011-12-09,392.85,393.62,394.04,391.03\n" + |
| 180 | "2011-12-12,391.68,391.84,393.90,389.45\n" + |
| 181 | "2011-12-13,393.00,388.81,395.40,387.10\n" + |
| 182 | "2011-12-14,386.70,380.19,387.38,377.68\n" + |
| 183 | "2011-12-15,383.33,378.94,383.74,378.31\n" + |
| 184 | "2011-12-16,380.36,381.02,384.15,379.57\n" + |
| 185 | "2011-12-19,382.47,382.21,384.85,380.48\n" + |
| 186 | "2011-12-20,387.76,395.95,396.10,387.26\n" + |
| 187 | "2011-12-21,396.69,396.45,397.30,392.01\n" + |
| 188 | "2011-12-22,397.00,398.55,399.13,396.10\n" + |
| 189 | "2011-12-23,399.69,403.33,403.59,399.49\n" + |
| 190 | "2011-12-27,403.10,406.53,409.09,403.02\n" + |
| 191 | "2011-12-28,406.89,402.64,408.25,401.34\n" + |
| 192 | "2011-12-29,403.40,405.12,405.65,400.51\n" + |
| 193 | "2011-12-30,403.51,405.00,406.28,403.49\n" + |
| 194 | "2012-01-03,409.50,411.23,412.50,409.00\n" + |
| 195 | "2012-01-04,410.21,413.44,414.68,409.28\n" + |
| 196 | "2012-01-05,414.95,418.03,418.55,412.67\n" + |
| 197 | "2012-01-06,419.77,422.40,422.75,419.22\n" + |
| 198 | "2012-01-09,425.52,421.73,427.75,421.35\n" + |
| 199 | "2012-01-10,425.91,423.24,426.00,421.50\n" + |
| 200 | "2012-01-11,422.59,422.55,422.85,419.31\n" + |
| 201 | "2012-01-12,422.41,421.39,422.90,418.75\n" + |
| 202 | "2012-01-13,419.53,419.81,420.45,418.66\n" + |
| 203 | "2012-01-17,424.20,424.70,425.99,422.96\n" + |
| 204 | "2012-01-18,426.87,429.11,429.47,426.30\n" + |
| 205 | "2012-01-19,430.03,427.75,431.37,426.51\n" + |
| 206 | "2012-01-20,427.49,420.30,427.50,419.75\n" + |
| 207 | "2012-01-23,422.67,427.41,428.45,422.30\n" + |
| 208 | "2012-01-24,425.10,420.41,425.10,419.55\n" + |
| 209 | "2012-01-25,454.26,446.66,454.45,443.73\n" + |
| 210 | "2012-01-26,448.45,444.63,448.79,443.14\n" + |
| 211 | "2012-01-27,444.37,447.28,448.48,443.77\n" + |
| 212 | "2012-01-30,445.71,453.01,453.90,445.39\n" + |
| 213 | "2012-01-31,455.85,456.48,458.24,453.07\n" + |
| 214 | "2012-02-01,458.49,456.19,458.99,455.55\n" + |
| 215 | "2012-02-02,455.90,455.12,457.17,453.98\n" + |
| 216 | "2012-02-03,457.30,459.68,460.00,455.56\n" + |
| 217 | "2012-02-06,458.38,463.97,464.98,458.20\n" + |
| 218 | "2012-02-07,465.25,468.83,469.75,464.58\n" + |
| 219 | "2012-02-08,470.50,476.68,476.79,469.70\n" + |
| 220 | "2012-02-09,480.95,493.17,496.75,480.56\n" + |
| 221 | "2012-02-10,491.17,493.42,497.62,488.55\n" + |
| 222 | "2012-02-13,499.74,502.60,503.83,497.09\n" + |
| 223 | "2012-02-14,504.70,509.46,509.56,502.00\n" ; |
| 224 | |
| 225 | g2 = new Dygraph( |
| 226 | document.getElementById("candlechart"), |
| 227 | candleData, |
| 228 | { |
| 229 | plotter: candlePlotter |
| 230 | }); |
| 231 | |
| 232 | |
| 233 | // Bar and Line chart |
| 234 | var short_data = data_nolabel(); |
| 235 | short_data = short_data.split('\n').slice(0, 20).join('\n'); |
| 236 | |
| 237 | g3 = new Dygraph( |
| 238 | document.getElementById("barlinechart"), |
| 239 | short_data, |
| 240 | { |
| 241 | labels: ['Date', 'A', 'B'], |
| 242 | includeZero: true, |
| 243 | "A": { |
| 244 | strokeWidth: 2 |
| 245 | }, |
| 246 | "B": { |
| 247 | plotter: barChartPlotter |
| 248 | } |
| 249 | }); |
| 250 | |
| 251 | |
| 252 | // Multiple column bar chart |
| 253 | function multiColumnBarPlotter(e) { |
| 254 | // We need to handle all the series simultaneously. |
| 255 | if (e.seriesIndex !== 0) return; |
| 256 | |
| 257 | var g = e.dygraph; |
| 258 | var ctx = e.drawingContext; |
| 259 | var sets = e.allSeriesPoints; |
| 260 | var y_bottom = e.dygraph.toDomYCoord(0); |
| 261 | |
| 262 | // Find the minimum separation between x-values. |
| 263 | // This determines the bar width. |
| 264 | var min_sep = Infinity; |
| 265 | for (var j = 0; j < sets.length; j++) { |
| 266 | var points = sets[j]; |
| 267 | for (var i = 1; i < points.length; i++) { |
| 268 | var sep = points[i].canvasx - points[i - 1].canvasx; |
| 269 | if (sep < min_sep) min_sep = sep; |
| 270 | } |
| 271 | } |
| 272 | var bar_width = Math.floor(2.0 / 3 * min_sep); |
| 273 | |
| 274 | var fillColors = []; |
| 275 | var strokeColors = g.getColors(); |
| 276 | for (var i = 0; i < strokeColors.length; i++) { |
| 277 | var color = new RGBColorParser(strokeColors[i]); |
| 278 | color.r = Math.floor((255 + color.r) / 2); |
| 279 | color.g = Math.floor((255 + color.g) / 2); |
| 280 | color.b = Math.floor((255 + color.b) / 2); |
| 281 | fillColors.push(color.toRGB()); |
| 282 | } |
| 283 | |
| 284 | for (var j = 0; j < sets.length; j++) { |
| 285 | ctx.fillStyle = fillColors[j]; |
| 286 | ctx.strokeStyle = strokeColors[j]; |
| 287 | for (var i = 0; i < sets[j].length; i++) { |
| 288 | var p = sets[j][i]; |
| 289 | var center_x = p.canvasx; |
| 290 | var x_left = center_x - (bar_width / 2) * (1 - j/(sets.length-1)); |
| 291 | |
| 292 | ctx.fillRect(x_left, p.canvasy, |
| 293 | bar_width/sets.length, y_bottom - p.canvasy); |
| 294 | |
| 295 | ctx.strokeRect(x_left, p.canvasy, |
| 296 | bar_width/sets.length, y_bottom - p.canvasy); |
| 297 | } |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | g4 = new Dygraph( |
| 302 | document.getElementById("multibar"), |
| 303 | short_data, |
| 304 | { |
| 305 | includeZero: true, |
| 306 | plotter: multiColumnBarPlotter |
| 307 | }); |
| 308 | |
| 309 | // Mixed Error Bars and Lines |
| 310 | g5 = new Dygraph( |
| 311 | document.getElementById("mixed-error"), |
| 312 | NoisyData(), |
| 313 | { |
| 314 | errorBars: true, |
| 315 | 'A': { |
| 316 | plotter: Dygraph.Plotters.errorPlotter |
| 317 | }, |
| 318 | 'B': { |
| 319 | plotter: Dygraph.Plotters.linePlotter, |
| 320 | strokePattern: Dygraph.DASHED_LINE |
| 321 | } |
| 322 | }); |
| 323 | |
| 324 | </script> |
| 325 | </body> |
| 326 | </html> |