lines.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  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 DashboardLines = function() {
  11. //
  12. // Setup module components
  13. //
  14. // App sales line chart
  15. var _AppSalesLinesChart = function(element, height) {
  16. if (typeof d3 == 'undefined' || typeof d3.tip == '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: 5, right: 30, bottom: 30, left: 50},
  27. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right,
  28. height = height - margin.top - margin.bottom;
  29. // Tooltip
  30. var tooltip = d3.tip()
  31. .attr('class', 'd3-tip')
  32. .html(function (d) {
  33. return '<ul class="list-unstyled mb-1">' +
  34. '<li>' + '<div class="font-size-base my-1"><i class="icon-circle-left2 mr-2"></i>' + d.name + '</div>' + '</li>' +
  35. '<li>' + '합계: &nbsp;' + '<span class="font-weight-semibold float-right">' + format_decimal(d.value, 'SalesAmtPoint') || 0 +'</span>' + '</li>' +
  36. '</ul>';
  37. });
  38. // Format date
  39. var parseDate = d3.time.format('%Y/%m/%d').parse,
  40. formatDate = d3.time.format('%b %d, %y');
  41. // Line colors
  42. var scale = ['#4CAF50', '#FF5722', '#5C6BC0'],
  43. color = d3.scale.ordinal().range(scale);
  44. // Create chart
  45. // ------------------------------
  46. // Container
  47. var container = d3Container.append('svg');
  48. // SVG element
  49. var svg = container
  50. .attr('width', width + margin.left + margin.right)
  51. .attr('height', height + margin.top + margin.bottom)
  52. .append('g')
  53. .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  54. .call(tooltip);
  55. // Add date range switcher
  56. // ------------------------------
  57. var menu = document.getElementById('select_date');
  58. menu.addEventListener('change', change);
  59. // Load data
  60. // ------------------------------
  61. // formatted = data;
  62. redraw();
  63. // Construct layout
  64. // ------------------------------
  65. // Add events
  66. var altKey;
  67. d3.select(window)
  68. .on('keydown', function() { altKey = d3.event.altKey; })
  69. .on('keyup', function() { altKey = false; });
  70. // Set terms of transition on date change
  71. function change() {
  72. d3.transition()
  73. .duration(altKey ? 7500 : 500)
  74. .each(redraw);
  75. }
  76. // Main chart drawing function
  77. // ------------------------------
  78. function redraw() {
  79. // Construct chart layout
  80. // ------------------------------
  81. // Create data nests
  82. // var nested = d3.nest()
  83. // .key(function(d) { return d.type; })
  84. // .map(formatted)
  85. var nested = {'val1': formatted}
  86. console.log(nested)
  87. // Get value from menu selection
  88. // the option values correspond
  89. //to the [type] value we used to nest the data
  90. var series = menu.value;
  91. // Only retrieve data from the selected series using nest
  92. var data = nested['val1'];
  93. // For object constancy we will need to set 'keys', one for each type of data (column name) exclude all others.
  94. color.domain(d3.keys(data[0]).filter(function(key) { return (key !== 'date' && key !== 'type'); }));
  95. // Setting up color map
  96. var linedata = color.domain().map(function(name) {
  97. return {
  98. name: name,
  99. values: data.map(function(d) {
  100. return {name: name, date: parseDate(d.date), value: parseFloat(d[name], 10)};
  101. })
  102. };
  103. });
  104. // Draw the line
  105. var line = d3.svg.line()
  106. .x(function(d) { return x(d.date); })
  107. .y(function(d) { return y(d.value); })
  108. .interpolate('cardinal');
  109. // Construct scales
  110. // ------------------------------
  111. // Horizontal
  112. var x = d3.time.scale()
  113. .domain([
  114. d3.min(linedata, function(c) { return d3.min(c.values, function(v) { return v.date; }); }),
  115. d3.max(linedata, function(c) { return d3.max(c.values, function(v) { return v.date; }); })
  116. ])
  117. .range([0, width]);
  118. // Vertical
  119. var y = d3.scale.linear()
  120. .domain([
  121. d3.min(linedata, function(c) { return d3.min(c.values, function(v) { return v.value; }); }),
  122. d3.max(linedata, function(c) { return d3.max(c.values, function(v) { return v.value; }); })
  123. ])
  124. .range([height, 0]);
  125. // Create axes
  126. // ------------------------------
  127. // Horizontal
  128. var xAxis = d3.svg.axis()
  129. .scale(x)
  130. .orient('bottom')
  131. .tickPadding(8)
  132. .ticks(d3.time.days)
  133. .innerTickSize(4)
  134. .tickFormat(d3.time.format('%m/%d'));
  135. // Vertical
  136. var yAxis = d3.svg.axis()
  137. .scale(y)
  138. .orient('left')
  139. .ticks(6)
  140. .tickSize(0 -width)
  141. .tickPadding(8);
  142. //
  143. // Append chart elements
  144. //
  145. // Append axes
  146. // ------------------------------
  147. // Horizontal
  148. svg.append('g')
  149. .attr('class', 'd3-axis d3-axis-horizontal')
  150. .attr('transform', 'translate(0,' + height + ')');
  151. // Vertical
  152. svg.append('g')
  153. .attr('class', 'd3-axis d3-axis-vertical d3-axis-transparent d3-grid d3-grid-dashed');
  154. // Append lines
  155. // ------------------------------
  156. // Bind the data
  157. var lines = svg.selectAll('.app-sales-lines')
  158. .data(linedata)
  159. // Append a group tag for each line
  160. var lineGroup = lines
  161. .enter()
  162. .append('g')
  163. .attr('class', 'app-sales-lines')
  164. .attr('id', function(d){ return d.name + '-line'; });
  165. // Append the line to the graph
  166. lineGroup.append('path')
  167. .attr('class', 'd3-line d3-line-medium')
  168. .style('stroke', function(d) { return color(d.name); })
  169. .style('opacity', 0)
  170. .attr('d', function(d) { return line(d.values[0]); })
  171. .transition()
  172. .duration(500)
  173. .delay(function(d, i) { return i * 200; })
  174. .style('opacity', 1);
  175. // Append circles
  176. // ------------------------------
  177. var circles = lines.selectAll('circle')
  178. .data(function(d) { return d.values; })
  179. .enter()
  180. .append('circle')
  181. .attr('class', 'd3-line-circle d3-line-circle-medium')
  182. .attr('cx', function(d,i){return x(d.date)})
  183. .attr('cy',function(d,i){return y(d.value)})
  184. .attr('r', 3)
  185. .style('stroke', function(d) { return color(d.name); });
  186. // Add transition
  187. circles
  188. .style('opacity', 0)
  189. .transition()
  190. .duration(500)
  191. .delay(500)
  192. .style('opacity', 1);
  193. // Append tooltip
  194. // ------------------------------
  195. // Add tooltip on circle hover
  196. circles
  197. .on('mouseover', function (d) {
  198. tooltip.offset([-15, 0]).show(d);
  199. // Animate circle radius
  200. d3.select(this).transition().duration(250).attr('r', 4);
  201. })
  202. .on('mouseout', function (d) {
  203. tooltip.hide(d);
  204. // Animate circle radius
  205. d3.select(this).transition().duration(250).attr('r', 3);
  206. });
  207. // Change tooltip direction of first point
  208. // to always keep it inside chart, useful on mobiles
  209. lines.each(function (d) {
  210. d3.select(d3.select(this).selectAll('circle')[0][0])
  211. .on('mouseover', function (d) {
  212. tooltip.offset([0, 15]).direction('e').show(d);
  213. // Animate circle radius
  214. d3.select(this).transition().duration(250).attr('r', 4);
  215. })
  216. .on('mouseout', function (d) {
  217. tooltip.direction('n').hide(d);
  218. // Animate circle radius
  219. d3.select(this).transition().duration(250).attr('r', 3);
  220. });
  221. })
  222. // Change tooltip direction of last point
  223. // to always keep it inside chart, useful on mobiles
  224. lines.each(function (d) {
  225. d3.select(d3.select(this).selectAll('circle')[0][d3.select(this).selectAll('circle').size() - 1])
  226. .on('mouseover', function (d) {
  227. tooltip.offset([0, -15]).direction('w').show(d);
  228. // Animate circle radius
  229. d3.select(this).transition().duration(250).attr('r', 4);
  230. })
  231. .on('mouseout', function (d) {
  232. tooltip.direction('n').hide(d);
  233. // Animate circle radius
  234. d3.select(this).transition().duration(250).attr('r', 3);
  235. })
  236. })
  237. // Update chart on date change
  238. // ------------------------------
  239. // Set variable for updating visualization
  240. var lineUpdate = d3.transition(lines);
  241. // Update lines
  242. lineUpdate.select('path')
  243. .attr('d', function(d, i) { return line(d.values); });
  244. // Update circles
  245. lineUpdate.selectAll('circle')
  246. .attr('cy',function(d,i){return y(d.value)})
  247. .attr('cx', function(d,i){return x(d.date)});
  248. // Update vertical axes
  249. d3.transition(svg)
  250. .select('.d3-axis-vertical')
  251. .call(yAxis);
  252. // Update horizontal axes
  253. d3.transition(svg)
  254. .select('.d3-axis-horizontal')
  255. .attr('transform', 'translate(0,' + height + ')')
  256. .call(xAxis);
  257. // Resize chart
  258. // ------------------------------
  259. // Call function on window resize
  260. window.addEventListener('resize', appSalesResize);
  261. // Call function on sidebar width change
  262. var sidebarToggle = document.querySelector('.sidebar-control');
  263. sidebarToggle && sidebarToggle.addEventListener('click', appSalesResize);
  264. // Resize function
  265. //
  266. // Since D3 doesn't support SVG resize by default,
  267. // we need to manually specify parts of the graph that need to
  268. // be updated on window resize
  269. function appSalesResize() {
  270. // Layout
  271. // -------------------------
  272. // Define width
  273. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right;
  274. // Main svg width
  275. container.attr('width', width + margin.left + margin.right);
  276. // Width of appended group
  277. svg.attr('width', width + margin.left + margin.right);
  278. // Horizontal range
  279. x.range([0, width]);
  280. // Vertical range
  281. y.range([height, 0]);
  282. // Chart elements
  283. // -------------------------
  284. // Horizontal axis
  285. svg.select('.d3-axis-horizontal').call(xAxis);
  286. // Vertical axis
  287. svg.select('.d3-axis-vertical').call(yAxis.tickSize(0-width));
  288. // Lines
  289. svg.selectAll('.d3-line').attr('d', function(d, i) { return line(d.values); });
  290. // Circles
  291. svg.selectAll('.d3-line-circle').attr('cx', function(d,i){return x(d.date)})
  292. }
  293. }
  294. }
  295. };
  296. // Daily revenue line chart
  297. var _DailyRevenueLineChart = function(element, height) {
  298. if (typeof d3 == 'undefined') {
  299. console.warn('Warning - d3.min.js is not loaded.');
  300. return;
  301. }
  302. // Initialize chart only if element exsists in the DOM
  303. if($(element).length > 0) {
  304. // Basic setup
  305. // ------------------------------
  306. // Add data set
  307. var dataset = [
  308. {
  309. 'date': '04/13/14',
  310. 'alpha': '60'
  311. }, {
  312. 'date': '04/14/14',
  313. 'alpha': '35'
  314. }, {
  315. 'date': '04/15/14',
  316. 'alpha': '65'
  317. }, {
  318. 'date': '04/16/14',
  319. 'alpha': '50'
  320. }, {
  321. 'date': '04/17/14',
  322. 'alpha': '65'
  323. }, {
  324. 'date': '04/18/14',
  325. 'alpha': '20'
  326. }, {
  327. 'date': '04/19/14',
  328. 'alpha': '60'
  329. }
  330. ];
  331. // Main variables
  332. var d3Container = d3.select(element),
  333. margin = {top: 0, right: 0, bottom: 0, left: 0},
  334. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right,
  335. height = height - margin.top - margin.bottom,
  336. padding = 20;
  337. // Format date
  338. var parseDate = d3.time.format('%m/%d/%y').parse,
  339. formatDate = d3.time.format('%a, %B %e');
  340. // Colors
  341. var lineColor = '#fff',
  342. guideColor = 'rgba(255,255,255,0.3)';
  343. // Add tooltip
  344. // ------------------------------
  345. var tooltip = d3.tip()
  346. .attr('class', 'd3-tip')
  347. .html(function (d) {
  348. return '<ul class="list-unstyled mb-1">' +
  349. '<li>' + '<div class="font-size-base my-1"><i class="icon-check2 mr-2"></i>' + formatDate(d.date) + '</div>' + '</li>' +
  350. '<li>' + 'Sales: &nbsp;' + '<span class="font-weight-semibold float-right">' + d.alpha + '</span>' + '</li>' +
  351. '<li>' + 'Revenue: &nbsp; ' + '<span class="font-weight-semibold float-right">' + '$' + (d.alpha * 25).toFixed(2) + '</span>' + '</li>' +
  352. '</ul>';
  353. });
  354. // Create chart
  355. // ------------------------------
  356. // Add svg element
  357. var container = d3Container.append('svg');
  358. // Add SVG group
  359. var svg = container
  360. .attr('width', width + margin.left + margin.right)
  361. .attr('height', height + margin.top + margin.bottom)
  362. .append('g')
  363. .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  364. .call(tooltip);
  365. // Load data
  366. // ------------------------------
  367. dataset.forEach(function (d) {
  368. d.date = parseDate(d.date);
  369. d.alpha = +d.alpha;
  370. });
  371. // Construct scales
  372. // ------------------------------
  373. // Horizontal
  374. var x = d3.time.scale()
  375. .range([padding, width - padding]);
  376. // Vertical
  377. var y = d3.scale.linear()
  378. .range([height, 5]);
  379. // Set input domains
  380. // ------------------------------
  381. // Horizontal
  382. x.domain(d3.extent(dataset, function (d) {
  383. return d.date;
  384. }));
  385. // Vertical
  386. y.domain([0, d3.max(dataset, function (d) {
  387. return Math.max(d.alpha);
  388. })]);
  389. // Construct chart layout
  390. // ------------------------------
  391. // Line
  392. var line = d3.svg.line()
  393. .x(function(d) {
  394. return x(d.date);
  395. })
  396. .y(function(d) {
  397. return y(d.alpha)
  398. });
  399. //
  400. // Append chart elements
  401. //
  402. // Add mask for animation
  403. // ------------------------------
  404. // Add clip path
  405. var clip = svg.append('defs')
  406. .append('clipPath')
  407. .attr('id', 'clip-line-small');
  408. // Add clip shape
  409. var clipRect = clip.append('rect')
  410. .attr('class', 'clip')
  411. .attr('width', 0)
  412. .attr('height', height);
  413. // Animate mask
  414. clipRect
  415. .transition()
  416. .duration(1000)
  417. .ease('linear')
  418. .attr('width', width);
  419. // Line
  420. // ------------------------------
  421. // Path
  422. var path = svg.append('path')
  423. .attr({
  424. 'd': line(dataset),
  425. 'clip-path': 'url(#clip-line-small)',
  426. 'class': 'd3-line d3-line-medium'
  427. })
  428. .style('stroke', lineColor);
  429. // Animate path
  430. svg.select('.line-tickets')
  431. .transition()
  432. .duration(1000)
  433. .ease('linear');
  434. // Add vertical guide lines
  435. // ------------------------------
  436. // Bind data
  437. var guide = svg.append('g')
  438. .selectAll('.d3-line-guides-group')
  439. .data(dataset);
  440. // Append lines
  441. guide
  442. .enter()
  443. .append('line')
  444. .attr('class', 'd3-line-guides')
  445. .attr('x1', function (d, i) {
  446. return x(d.date);
  447. })
  448. .attr('y1', function (d, i) {
  449. return height;
  450. })
  451. .attr('x2', function (d, i) {
  452. return x(d.date);
  453. })
  454. .attr('y2', function (d, i) {
  455. return height;
  456. })
  457. .style('stroke', guideColor)
  458. .style('stroke-dasharray', '4,2')
  459. .style('shape-rendering', 'crispEdges');
  460. // Animate guide lines
  461. guide
  462. .transition()
  463. .duration(1000)
  464. .delay(function(d, i) { return i * 150; })
  465. .attr('y2', function (d, i) {
  466. return y(d.alpha);
  467. });
  468. // Alpha app points
  469. // ------------------------------
  470. // Add points
  471. var points = svg.insert('g')
  472. .selectAll('.d3-line-circle')
  473. .data(dataset)
  474. .enter()
  475. .append('circle')
  476. .attr('class', 'd3-line-circle d3-line-circle-medium')
  477. .attr('cx', line.x())
  478. .attr('cy', line.y())
  479. .attr('r', 3)
  480. .style('stroke', lineColor)
  481. .style('fill', lineColor);
  482. // Animate points on page load
  483. points
  484. .style('opacity', 0)
  485. .transition()
  486. .duration(250)
  487. .ease('linear')
  488. .delay(1000)
  489. .style('opacity', 1);
  490. // Add user interaction
  491. points
  492. .on('mouseover', function (d) {
  493. tooltip.offset([-10, 0]).show(d);
  494. // Animate circle radius
  495. d3.select(this).transition().duration(250).attr('r', 4);
  496. })
  497. // Hide tooltip
  498. .on('mouseout', function (d) {
  499. tooltip.hide(d);
  500. // Animate circle radius
  501. d3.select(this).transition().duration(250).attr('r', 3);
  502. });
  503. // Change tooltip direction of first point
  504. d3.select(points[0][0])
  505. .on('mouseover', function (d) {
  506. tooltip.offset([0, 10]).direction('e').show(d);
  507. // Animate circle radius
  508. d3.select(this).transition().duration(250).attr('r', 4);
  509. })
  510. .on('mouseout', function (d) {
  511. tooltip.direction('n').hide(d);
  512. // Animate circle radius
  513. d3.select(this).transition().duration(250).attr('r', 3);
  514. });
  515. // Change tooltip direction of last point
  516. d3.select(points[0][points.size() - 1])
  517. .on('mouseover', function (d) {
  518. tooltip.offset([0, -10]).direction('w').show(d);
  519. // Animate circle radius
  520. d3.select(this).transition().duration(250).attr('r', 4);
  521. })
  522. .on('mouseout', function (d) {
  523. tooltip.direction('n').hide(d);
  524. // Animate circle radius
  525. d3.select(this).transition().duration(250).attr('r', 3);
  526. })
  527. // Resize chart
  528. // ------------------------------
  529. // Call function on window resize
  530. window.addEventListener('resize', revenueResize);
  531. // Call function on sidebar width change
  532. var sidebarToggle = document.querySelector('.sidebar-control');
  533. sidebarToggle && sidebarToggle.addEventListener('click', revenueResize);
  534. // Resize function
  535. //
  536. // Since D3 doesn't support SVG resize by default,
  537. // we need to manually specify parts of the graph that need to
  538. // be updated on window resize
  539. function revenueResize() {
  540. // Layout variables
  541. width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right;
  542. // Layout
  543. // -------------------------
  544. // Main svg width
  545. container.attr('width', width + margin.left + margin.right);
  546. // Width of appended group
  547. svg.attr('width', width + margin.left + margin.right);
  548. // Horizontal range
  549. x.range([padding, width - padding]);
  550. // Chart elements
  551. // -------------------------
  552. // Mask
  553. clipRect.attr('width', width);
  554. // Line path
  555. svg.selectAll('.d3-line').attr('d', line(dataset));
  556. // Circles
  557. svg.selectAll('.d3-line-circle').attr('cx', line.x());
  558. // Guide lines
  559. svg.selectAll('.d3-line-guides')
  560. .attr('x1', function (d, i) {
  561. return x(d.date);
  562. })
  563. .attr('x2', function (d, i) {
  564. return x(d.date);
  565. });
  566. }
  567. }
  568. };
  569. //
  570. // Return objects assigned to module
  571. //
  572. return {
  573. init: function() {
  574. _AppSalesLinesChart('#app_sales', 255);
  575. _DailyRevenueLineChart('#today-revenue', 50);
  576. }
  577. }
  578. }();
  579. // Initialize module
  580. // ------------------------------
  581. document.addEventListener('DOMContentLoaded', function() {
  582. DashboardLines.init();
  583. });