areas.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /* ------------------------------------------------------------------------------
  2. *
  3. * # D3.js - horizontal bar chart
  4. *
  5. * Demo d3.js horizontal bar chart setup with .csv data source
  6. *
  7. * ---------------------------------------------------------------------------- */
  8. // Setup module
  9. // ------------------------------
  10. var DashboardAreas = function() {
  11. //
  12. // Setup module components
  13. //
  14. // Monthly sales area chart
  15. var _MonthlySalesAreaChart = function(element, height, color) {
  16. if (typeof d3 == 'undefined') {
  17. console.warn('Warning - d3.min.js is not loaded.');
  18. return;
  19. }
  20. // Initialize chart only if element exsists in the DOM
  21. if($(element).length > 0) {
  22. // Basic setup
  23. // ------------------------------
  24. // Define main variables
  25. var d3Container = d3.select(element),
  26. margin = {top: 20, right: 35, bottom: 40, left: 35},
  27. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right,
  28. height = height - margin.top - margin.bottom;
  29. // Date and time format
  30. var parseDate = d3.time.format('%Y-%m-%d').parse,
  31. bisectDate = d3.bisector(function(d) { return d.date; }).left,
  32. formatDate = d3.time.format('%b %d');
  33. // Create SVG
  34. // ------------------------------
  35. // Container
  36. var container = d3Container.append('svg');
  37. // SVG element
  38. var svg = container
  39. .attr('width', width + margin.left + margin.right)
  40. .attr('height', height + margin.top + margin.bottom)
  41. .append('g')
  42. .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  43. // Construct chart layout
  44. // ------------------------------
  45. // Area
  46. var area = d3.svg.area()
  47. .x(function(d) { return x(d.date); })
  48. .y0(height)
  49. .y1(function(d) { return y(d.value); })
  50. .interpolate('monotone')
  51. // Construct scales
  52. // ------------------------------
  53. // Horizontal
  54. var x = d3.time.scale().range([0, width ]);
  55. // Vertical
  56. var y = d3.scale.linear().range([height, 0]);
  57. // Create axes
  58. // ------------------------------
  59. // Horizontal
  60. var xAxis = d3.svg.axis()
  61. .scale(x)
  62. .orient('bottom')
  63. .ticks(d3.time.days, 6)
  64. .innerTickSize(4)
  65. .tickPadding(8)
  66. .tickFormat(d3.time.format('%b %d'));
  67. // Load data
  68. // ------------------------------
  69. d3.json('../../../../global_assets/demo_data/dashboard/monthly_sales.json', function (error, data) {
  70. // Show what's wrong if error
  71. if (error) return console.error(error);
  72. // Pull out values
  73. data.forEach(function (d) {
  74. d.date = parseDate(d.date);
  75. d.value = +d.value;
  76. });
  77. // Get the maximum value in the given array
  78. var maxY = d3.max(data, function(d) { return d.value; });
  79. // Reset start data for animation
  80. var startData = data.map(function(datum) {
  81. return {
  82. date: datum.date,
  83. value: 0
  84. };
  85. });
  86. // Set input domains
  87. // ------------------------------
  88. // Horizontal
  89. x.domain(d3.extent(data, function(d, i) { return d.date; }));
  90. // Vertical
  91. y.domain([0, d3.max( data, function(d) { return d.value; })]);
  92. //
  93. // Append chart elements
  94. //
  95. // Append axes
  96. // -------------------------
  97. // Horizontal
  98. var horizontalAxis = svg.append('g')
  99. .attr('class', 'd3-axis d3-axis-horizontal')
  100. .attr('transform', 'translate(0,' + height + ')')
  101. .call(xAxis);
  102. // Add extra subticks for hidden hours
  103. horizontalAxis.selectAll('.d3-axis-subticks')
  104. .data(x.ticks(d3.time.days), function(d) { return d; })
  105. .enter()
  106. .append('line')
  107. .attr('class', 'd3-axis-subticks')
  108. .attr('y1', 0)
  109. .attr('y2', 4)
  110. .attr('x1', x)
  111. .attr('x2', x);
  112. // Append area
  113. // -------------------------
  114. // Add area path
  115. svg.append('path')
  116. .datum(data)
  117. .attr('class', 'd3-area')
  118. .attr('d', area)
  119. .style('fill', color)
  120. .transition() // begin animation
  121. .duration(1000)
  122. .attrTween('d', function() {
  123. var interpolator = d3.interpolateArray(startData, data);
  124. return function (t) {
  125. return area(interpolator (t));
  126. }
  127. });
  128. // Append crosshair and tooltip
  129. // -------------------------
  130. //
  131. // Line
  132. //
  133. // Line group
  134. var focusLine = svg.append('g')
  135. .style('display', 'none');
  136. // Line element
  137. focusLine.append('line')
  138. .attr('class', 'vertical-crosshair d3-crosshair-line')
  139. .attr('y1', 0)
  140. .attr('y2', -maxY);
  141. //
  142. // Pointer
  143. //
  144. // Pointer group
  145. var focusPointer = svg.append('g')
  146. .attr('class', 'd3-crosshair-pointer')
  147. .style('display', 'none');
  148. // Pointer element
  149. focusPointer.append('circle')
  150. .attr('class', 'd3-line-circle')
  151. .attr('r', 3)
  152. .style('stroke', color)
  153. .style('stroke-width', 1.5);
  154. //
  155. // Text
  156. //
  157. // Text group
  158. var focusText = svg.append('g')
  159. .attr('class', 'd3-crosshair-text')
  160. .style('display', 'none');
  161. // Text element
  162. focusText.append('text')
  163. .attr('class', 'd3-text')
  164. .attr('dy', -10)
  165. .style('font-size', 12);
  166. //
  167. // Overlay with events
  168. //
  169. svg.append('rect')
  170. .attr('class', 'd3-crosshair-overlay')
  171. .style('fill', 'none')
  172. .style('pointer-events', 'all')
  173. .attr('width', width)
  174. .attr('height', height)
  175. .on('mouseover', function() {
  176. focusPointer.style('display', null);
  177. focusLine.style('display', null)
  178. focusText.style('display', null);
  179. })
  180. .on('mouseout', function() {
  181. focusPointer.style('display', 'none');
  182. focusLine.style('display', 'none');
  183. focusText.style('display', 'none');
  184. })
  185. .on('mousemove', mousemove);
  186. // Display tooltip on mousemove
  187. function mousemove() {
  188. // Define main variables
  189. var mouse = d3.mouse(this),
  190. mousex = mouse[0],
  191. mousey = mouse[1],
  192. x0 = x.invert(mousex),
  193. i = bisectDate(data, x0),
  194. d0 = data[i - 1],
  195. d1 = data[i],
  196. d = x0 - d0.date > d1.date - x0 ? d1 : d0;
  197. // Move line
  198. focusLine.attr('transform', 'translate(' + x(d.date) + ',' + height + ')');
  199. // Move pointer
  200. focusPointer.attr('transform', 'translate(' + x(d.date) + ',' + y(d.value) + ')');
  201. // Reverse tooltip at the end point
  202. if(mousex >= (d3Container.node().getBoundingClientRect().width - focusText.select('text').node().getBoundingClientRect().width - margin.right - margin.left)) {
  203. focusText.select('text').attr('text-anchor', 'end').attr('x', function () { return (x(d.date) - 15) + 'px' }).text(formatDate(d.date) + ' - ' + d.value + ' sales');
  204. }
  205. else {
  206. focusText.select('text').attr('text-anchor', 'start').attr('x', function () { return (x(d.date) + 15) + 'px' }).text(formatDate(d.date) + ' - ' + d.value + ' sales');
  207. }
  208. }
  209. // Resize chart
  210. // ------------------------------
  211. // Call function on window resize
  212. window.addEventListener('resize', monthlySalesAreaResize);
  213. // Call function on sidebar width change
  214. var sidebarToggle = document.querySelector('.sidebar-control');
  215. sidebarToggle && sidebarToggle.addEventListener('click', monthlySalesAreaResize);
  216. // Resize function
  217. //
  218. // Since D3 doesn't support SVG resize by default,
  219. // we need to manually specify parts of the graph that need to
  220. // be updated on window resize
  221. function monthlySalesAreaResize() {
  222. // Layout variables
  223. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right;
  224. // Layout
  225. // -------------------------
  226. // Main svg width
  227. container.attr('width', width + margin.left + margin.right);
  228. // Width of appended group
  229. svg.attr('width', width + margin.left + margin.right);
  230. // Axes
  231. // -------------------------
  232. // Horizontal range
  233. x.range([0, width]);
  234. // Horizontal axis
  235. svg.selectAll('.d3-axis-horizontal').call(xAxis);
  236. // Horizontal axis subticks
  237. svg.selectAll('.d3-axis-subticks').attr('x1', x).attr('x2', x);
  238. // Chart elements
  239. // -------------------------
  240. // Area path
  241. svg.selectAll('.d3-area').datum(data).attr('d', area);
  242. // Crosshair
  243. svg.selectAll('.d3-crosshair-overlay').attr('width', width);
  244. }
  245. });
  246. }
  247. };
  248. // Messages area chart
  249. var _MessagesAreaChart = function(element, height, color) {
  250. if (typeof d3 == 'undefined') {
  251. console.warn('Warning - d3.min.js is not loaded.');
  252. return;
  253. }
  254. // Initialize chart only if element exsists in the DOM
  255. if($(element).length > 0) {
  256. // Basic setup
  257. // ------------------------------
  258. // Define main variables
  259. var d3Container = d3.select(element),
  260. margin = {top: 0, right: 0, bottom: 0, left: 0},
  261. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right,
  262. height = height - margin.top - margin.bottom;
  263. // Date and time format
  264. var parseDate = d3.time.format('%Y-%m-%d').parse;
  265. // Create SVG
  266. // ------------------------------
  267. // Container
  268. var container = d3Container.append('svg');
  269. // SVG element
  270. var svg = container
  271. .attr('width', width + margin.left + margin.right)
  272. .attr('height', height + margin.top + margin.bottom)
  273. .append('g')
  274. .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  275. // Construct chart layout
  276. // ------------------------------
  277. // Area
  278. var area = d3.svg.area()
  279. .x(function(d) { return x(d.date); })
  280. .y0(height)
  281. .y1(function(d) { return y(d.value); })
  282. .interpolate('monotone')
  283. // Construct scales
  284. // ------------------------------
  285. // Horizontal
  286. var x = d3.time.scale().range([0, width ]);
  287. // Vertical
  288. var y = d3.scale.linear().range([height, 0]);
  289. // Load data
  290. // ------------------------------
  291. d3.json('../../../../global_assets/demo_data/dashboard/monthly_sales.json', function (error, data) {
  292. // Show what's wrong if error
  293. if (error) return console.error(error);
  294. // Pull out values
  295. data.forEach(function (d) {
  296. d.date = parseDate(d.date);
  297. d.value = +d.value;
  298. });
  299. // Get the maximum value in the given array
  300. var maxY = d3.max(data, function(d) { return d.value; });
  301. // Reset start data for animation
  302. var startData = data.map(function(datum) {
  303. return {
  304. date: datum.date,
  305. value: 0
  306. };
  307. });
  308. // Set input domains
  309. // ------------------------------
  310. // Horizontal
  311. x.domain(d3.extent(data, function(d, i) { return d.date; }));
  312. // Vertical
  313. y.domain([0, d3.max( data, function(d) { return d.value; })]);
  314. //
  315. // Append chart elements
  316. //
  317. // Add area path
  318. svg.append('path')
  319. .datum(data)
  320. .attr('class', 'd3-area')
  321. .style('fill', color)
  322. .attr('d', area)
  323. .transition() // begin animation
  324. .duration(1000)
  325. .attrTween('d', function() {
  326. var interpolator = d3.interpolateArray(startData, data);
  327. return function (t) {
  328. return area(interpolator (t));
  329. }
  330. });
  331. // Resize chart
  332. // ------------------------------
  333. // Call function on window resize
  334. window.addEventListener('resize', messagesAreaResize);
  335. // Call function on sidebar width change
  336. var sidebarToggle = document.querySelector('.sidebar-control');
  337. sidebarToggle && sidebarToggle.addEventListener('click', messagesAreaResize);
  338. // Resize function
  339. //
  340. // Since D3 doesn't support SVG resize by default,
  341. // we need to manually specify parts of the graph that need to
  342. // be updated on window resize
  343. function messagesAreaResize() {
  344. // Layout variables
  345. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right;
  346. // Layout
  347. // -------------------------
  348. // Main svg width
  349. container.attr('width', width + margin.left + margin.right);
  350. // Width of appended group
  351. svg.attr('width', width + margin.left + margin.right);
  352. // Horizontal range
  353. x.range([0, width]);
  354. // Chart elements
  355. // -------------------------
  356. // Area path
  357. svg.selectAll('.d3-area').datum( data ).attr('d', area);
  358. }
  359. });
  360. }
  361. };
  362. //
  363. // Return objects assigned to module
  364. //
  365. return {
  366. init: function() {
  367. _MonthlySalesAreaChart('#monthly-sales-stats', 100, '#4DB6AC');
  368. _MessagesAreaChart('#messages-stats', 40, '#5C6BC0');
  369. }
  370. }
  371. }();
  372. // Initialize module
  373. // ------------------------------
  374. document.addEventListener('DOMContentLoaded', function() {
  375. DashboardAreas.init();
  376. });