Time Series Chart with Zoom Controls
30 Days
60 Days
90 Days
From:
1/31/19
To:
6/17/19
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ZingSoft Demo</title> <script nonce="undefined" src="https://cdn.zingchart.com/zingchart.min.js"></script> <style> .zc-body { background-color: #FFF; font: 100%/1.5 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif, 'Comic Sans'; } .chart--container { background-color: #FFF; min-height: 530px; width: 100%; height: 100%; } #day { color: #2196F3; } #day { margin-top: 20px; margin-right: 20px; font-size: 25px; float: right; } #date-picker-container { margin-left: 25px; } [data-period] { margin: 5px; padding: 10px 10px; background-color: #2196F3; color: #FFF; display: inline-block; } [data-period]:hover { cursor: pointer; opacity: .7; } #date-picker-container div { display: inline-block; } #date-from, #date-to { margin-right: 6px; margin-left: 6px; color: #2196F3; } .active { background: #fd992b; } .zc-ref { display: none; } </style> </head> <body> <div id="myChart" class="chart--container"> <div id="date-picker-container"> <span data-period="1" class="active">30 Days</span> <span data-period="2">60 Days</span> <span data-period="3">90 Days</span> <div> <b>From:</b> <span id="date-from">1/31/19</span> </div> <div> <b>To:</b> <span id="date-to">6/17/19</span> </div> <div id="day"></div> </div> <div id="main-container"> <a class="zc-ref" href="https://www.zingchart.com/">Powered by ZingChart</a> </div> </div> <script> ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"]; let chartConfig = { type: 'mixed', title: { text: 'Projected Cash Flow', align: 'center', }, plot: { tooltip: { visible: false, }, aspect: 'spline', marker: { visible: false, }, }, plotarea: {}, scaleX: { values: [ '1/31/19', '2/10/19', '2/11/19', '2/12/19', '2/13/19', '2/19/19', '2/18/19', '2/19/19', '2/20/19', '2/21/19', '2/24/19', '2/25/19', '2/26/19', '2/27/19', '2/28/19', '2/3/19', '2/4/19', '2/5/19', '2/6/19', '2/7/19', '3/10/19', '3/11/19', '3/12/19', '3/13/19', '3/14/19', '3/17/19', '3/18/19', '3/19/19', '3/20/19', '3/21/19', '3/24/19', '3/25/19', '3/26/19', '3/27/19', '3/28/19', '3/3/19', '3/31/19', '3/4/19', '3/5/19', '3/6/19', '3/7/19', '4/1/19', '4/10/19', '4/11/19', '4/14/19', '4/15/19', '4/16/19', '4/17/19', '4/2/19', '4/21/19', '4/22/19', '4/23/19', '4/24/19', '4/25/19', '4/28/19', '4/29/19', '4/3/19', '4/30/19', '4/4/19', '4/7/19', '4/8/19', '4/9/19', '5/1/19', '5/12/19', '5/13/19', '5/14/19', '5/15/19', '5/16/19', '5/19/19', '5/20/19', '5/21/19', '5/22/19', '5/23/19', '5/27/19', '5/28/19', '5/29/19', '5/30/19', '5/5/19', '5/6/19', '5/7/19', '5/8/19', '5/9/19', '6/10/19', '6/11/19', '6/12/19', '6/13/19', '6/16/19', '6/17/19', ], itemsOverlap: true, lineWidth: '0px', tick: { alpha: 1, lineColor: '#2196F3', lineWidth: '2px', }, zooming: true, zoomTo: [0, 30], }, scaleY: { values: '0:20000:1000', guide: { visible: false, }, }, scaleY2: {}, crosshairX: { lineColor: '#2196F3', lineStyle: 'dashed', lineWidth: '2px', marker: { type: 'triangle', size: '5px', visible: true, }, plotLabel: { visible: false, }, }, preview: { live: true, }, series: [{ type: 'bar', values: [ 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 8000, 0, 5000, ], backgroundColor: '#FFEB3B', scales: 'scale-x, scale-y-2', }, { type: 'line', values: [ 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 15000, ], lineColor: '#2196F3', scales: 'scale-x, scale-y-2', }, ], }; zingchart.render({ id: 'myChart', data: chartConfig, width: '100%', }); /** * As we are moving the guide, we want to update the items outside * of the chart. We could do this inside the chart, but since JS * is single threaded we don't want to block UI with a chart * update. A chart update is more expensive than a node update. * You may see no performance gains on a dataset this size, but * with some increase its possible to see a discrepancy for the * user. This is why I chose to contstruct the items outside * of the graph. */ zingchart.guide_mousemove = (e) => { if (e['scale-label']) { document.getElementById('day').innerHTML = 'Day: ' + e['scale-label']['scale-x']; } }; /*********** Functions ***********/ /** * ZingChart defined event listener. Will capture ZoomEvent and related * Zooming Information. */ zingchart.zoom = (e) => { displayZoomValues(e.kmin, e.kmax); }; /** * Apply dates to display current zoomed dates */ let displayZoomValues = (sFrom, sTo) => { let dateFrom = document.getElementById('date-from'); let dateTo = document.getElementById('date-to'); // If viewall is clicked show the default dates if (!sFrom) { sFrom = '1/31/19'; } if (!sTo) { sTo = '6/17/19'; } dateFrom.innerHTML = sFrom; dateTo.innerHTML = sTo; }; /** * Apply zoom to graph. */ let zoomToIndex = (max) => { // ZingChart api automated zoom. Have to be careful // not to zoom past the end of the graph zingchart.exec('myChart', 'zoomto', { xmax: max, xmin: 0, }); }; /** * Capture the spans clicks with data-period attribute. */ document .getElementById('date-picker-container') .addEventListener('click', (e) => { let target = e.target; // Only capture spans with data-period attribute if (target.dataset['period']) { // Toggle classes removeActiveClass(); e.target.classList.toggle('active'); switch (target.dataset['period']) { case '1': zoomToIndex(30); break; case '2': zoomToIndex(60); break; default: zingchart.exec('myChart', 'viewall'); break; } } }); let removeActiveClass = () => { let activeElement = document.querySelector('[data-period].active'); activeElement.classList.toggle('active'); }; </script> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ZingSoft Demo</title> <script src="https://cdn.zingchart.com/zingchart.min.js"></script> </head> <body> <div id="myChart" class="chart--container"> <div id="date-picker-container"> <span data-period="1" class="active">30 Days</span> <span data-period="2">60 Days</span> <span data-period="3">90 Days</span> <div> <b>From:</b> <span id="date-from">1/31/19</span> </div> <div> <b>To:</b> <span id="date-to">6/17/19</span> </div> <div id="day"></div> </div> <div id="main-container"> <a class="zc-ref" href="https://www.zingchart.com/">Powered by ZingChart</a> </div> </div> </body> </html>
let chartConfig = { type: 'mixed', title: { text: 'Projected Cash Flow', align: 'center', }, plot: { tooltip: { visible: false, }, aspect: 'spline', marker: { visible: false, }, }, plotarea: {}, scaleX: { values: [ '1/31/19', '2/10/19', '2/11/19', '2/12/19', '2/13/19', '2/19/19', '2/18/19', '2/19/19', '2/20/19', '2/21/19', '2/24/19', '2/25/19', '2/26/19', '2/27/19', '2/28/19', '2/3/19', '2/4/19', '2/5/19', '2/6/19', '2/7/19', '3/10/19', '3/11/19', '3/12/19', '3/13/19', '3/14/19', '3/17/19', '3/18/19', '3/19/19', '3/20/19', '3/21/19', '3/24/19', '3/25/19', '3/26/19', '3/27/19', '3/28/19', '3/3/19', '3/31/19', '3/4/19', '3/5/19', '3/6/19', '3/7/19', '4/1/19', '4/10/19', '4/11/19', '4/14/19', '4/15/19', '4/16/19', '4/17/19', '4/2/19', '4/21/19', '4/22/19', '4/23/19', '4/24/19', '4/25/19', '4/28/19', '4/29/19', '4/3/19', '4/30/19', '4/4/19', '4/7/19', '4/8/19', '4/9/19', '5/1/19', '5/12/19', '5/13/19', '5/14/19', '5/15/19', '5/16/19', '5/19/19', '5/20/19', '5/21/19', '5/22/19', '5/23/19', '5/27/19', '5/28/19', '5/29/19', '5/30/19', '5/5/19', '5/6/19', '5/7/19', '5/8/19', '5/9/19', '6/10/19', '6/11/19', '6/12/19', '6/13/19', '6/16/19', '6/17/19', ], itemsOverlap: true, lineWidth: '0px', tick: { alpha: 1, lineColor: '#2196F3', lineWidth: '2px', }, zooming: true, zoomTo: [0, 30], }, scaleY: { values: '0:20000:1000', guide: { visible: false, }, }, scaleY2: {}, crosshairX: { lineColor: '#2196F3', lineStyle: 'dashed', lineWidth: '2px', marker: { type: 'triangle', size: '5px', visible: true, }, plotLabel: { visible: false, }, }, preview: { live: true, }, series: [ { type: 'bar', values: [ 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 4000, 10000, 5000, 2000, 6000, 10000, 15000, 8000, 3000, 7000, 13000, 8000, 0, 5000, ], backgroundColor: '#FFEB3B', scales: 'scale-x, scale-y-2', }, { type: 'line', values: [ 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 3000, 4000, 10000, 13000, 5000, 0, 9000, 16000, 9000, 6000, 7000, 15000, ], lineColor: '#2196F3', scales: 'scale-x, scale-y-2', }, ], }; zingchart.render({ id: 'myChart', data: chartConfig, width: '100%', }); /** * As we are moving the guide, we want to update the items outside * of the chart. We could do this inside the chart, but since JS * is single threaded we don't want to block UI with a chart * update. A chart update is more expensive than a node update. * You may see no performance gains on a dataset this size, but * with some increase its possible to see a discrepancy for the * user. This is why I chose to contstruct the items outside * of the graph. */ zingchart.guide_mousemove = (e) => { if (e['scale-label']) { document.getElementById('day').innerHTML = 'Day: ' + e['scale-label']['scale-x']; } }; /*********** Functions ***********/ /** * ZingChart defined event listener. Will capture ZoomEvent and related * Zooming Information. */ zingchart.zoom = (e) => { displayZoomValues(e.kmin, e.kmax); }; /** * Apply dates to display current zoomed dates */ let displayZoomValues = (sFrom, sTo) => { let dateFrom = document.getElementById('date-from'); let dateTo = document.getElementById('date-to'); // If viewall is clicked show the default dates if (!sFrom) { sFrom = '1/31/19'; } if (!sTo) { sTo = '6/17/19'; } dateFrom.innerHTML = sFrom; dateTo.innerHTML = sTo; }; /** * Apply zoom to graph. */ let zoomToIndex = (max) => { // ZingChart api automated zoom. Have to be careful // not to zoom past the end of the graph zingchart.exec('myChart', 'zoomto', { xmax: max, xmin: 0, }); }; /** * Capture the spans clicks with data-period attribute. */ document .getElementById('date-picker-container') .addEventListener('click', (e) => { let target = e.target; // Only capture spans with data-period attribute if (target.dataset['period']) { // Toggle classes removeActiveClass(); e.target.classList.toggle('active'); switch (target.dataset['period']) { case '1': zoomToIndex(30); break; case '2': zoomToIndex(60); break; default: zingchart.exec('myChart', 'viewall'); break; } } }); let removeActiveClass = () => { let activeElement = document.querySelector('[data-period].active'); activeElement.classList.toggle('active'); };
.zc-body { background-color: #FFF; font: 100%/1.5 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif, 'Comic Sans'; } .chart--container { background-color: #FFF; min-height: 530px; width: 100%; height: 100%; } #day{ color: #2196F3; } #day { margin-top: 20px; margin-right: 20px; font-size: 25px; float: right; } #date-picker-container { margin-left: 25px; } [data-period] { margin: 5px; padding: 10px 10px; background-color: #2196F3; color: #FFF; display: inline-block; } [data-period]:hover { cursor: pointer; opacity: .7; } #date-picker-container div { display: inline-block; } #date-from, #date-to { margin-right: 6px; margin-left: 6px; color: #2196F3; } .active { background: #fd992b; } .zc-ref { display: none; }