Scrolling & Range Selectors

Viewing large charts can be difficult in compact user interfaces. Hence, the AxisScrollbar gives you an option of adding scrollbar to the axis of the chart.

This sample allows you to view the chart with the scrollbars on X and Y axis of the chart.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as chart from '@grapecity/wijmo.chart'; import { getData } from './data'; import { AxisScrollbar } from './AxisScrollbar'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // create the chart let theChart = new chart.FlexChart('#theChart', { plotMargin: 'NaN NaN NaN 80', bindingX: 'date', chartType: 'Line', itemsSource: getData(), tooltip: { content: '' }, axisX: { axisLine: false }, series: [ { name: 'Data', binding: 'yval' } ], }); // create Scrollbar let axisXScrollbar = new AxisScrollbar(theChart.axes[0], { minScale: 0.02 }); let axisYScrollbar = new AxisScrollbar(theChart.axes[1], { buttonsVisible: false, minScale: 0.05 }); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Scrolling & Range Selectors</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div id="theChart"></div> </div> </body> </html>
// generate some random data export function getData() { let dataCount = 3000; let data = []; for (var i = 0; i < dataCount; i++) { var mod = Math.floor(i / 10) % 10; data.push({ date: new Date(2005, 0, i), yval: mod == 0 ? null : Math.random() * 100 }); } return data; }
.wj-flexchart { margin: 0px; padding: 0px; border-bottom: 0px; touch-action: none; -ms-touch-action: none; padding-bottom: 40px; } body { margin-bottom: 24pt; } /**** Axis Scrollbar Css *****/ .wj-flexchart .wj-axis-scrollbar-container { position: relative; } .wj-flexchart .wj-axis-scrollbar-container .wj-chart-rangeslider { border: 1px solid #EAEAEA; background-color: #EAEAEA; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; border-top-left-radius: 6px; border-top-right-radius: 6px; } .wj-flexchart .wj-axis-scrollbar-container .wj-chart-hrangeslider { height: 20px; } .wj-flexchart .wj-axis-scrollbar-container .wj-chart-vrangeslider { width: 20px; }
import * as core from '@grapecity/wijmo'; import * as chart from '@grapecity/wijmo.chart'; import * as interaction from '@grapecity/wijmo.chart.interaction'; 'use strict'; /** * The @see:AxisScrollbar control displays a scrollbar that allows the user to * choose the range of Axis. */ export class AxisScrollbar { /** * Initializes a new instance of the @see:AxisScrollbar control. * * @param axis The axis that displays scrollbar. * @param options A JavaScript object containing initialization data for the control. */ constructor(axis, options) { // fields this._isVisible = true; this._min = null; this._max = null; this._buttonsVisible = true; //private _minScale: number = 0.01; this._minScale = 0; // host this._chart = null; this._axis = null; this._rangeSlider = null; // elements this._slbarContainer = null; this._isXAxis = true; this._chartRefreshDelay = null; if (!chart) { core.assert(false, 'The Axis cannot be null.'); } this._axis = axis; this._chart = axis._chart; this._isXAxis = this._axis.axisType === chart.AxisType.X; core.copy(this, options); this._createScrollbar(); } /** * Gets or sets the decrease button and increase button is visible or not. */ get buttonsVisible() { return this._buttonsVisible; } set buttonsVisible(value) { if (value !== this._buttonsVisible) { this._buttonsVisible = core.asBoolean(value); if (!this._rangeSlider) { return; } this._rangeSlider.buttonsVisible = this._buttonsVisible; } } /** * Gets or sets the visibility of the Axis scrollbar. */ get isVisible() { return this._isVisible; } set isVisible(value) { if (value != this._isVisible) { this._isVisible = core.asBoolean(value); if (!this._rangeSlider) { return; } this._rangeSlider.isVisible = value; } } set minPos(value) { if (value < 0 && value > 1 && value > this._rangeSlider._maxPos) { return; } this._rangeSlider._minPos = value; this._updateAxisRange(); } set maxPos(value) { if (value < 0 && value > 1 && value < this._rangeSlider._minPos) { return; } this._rangeSlider._maxPos = value; this._updateAxisRange(); } /** * Gets or sets the minimum range scale of the Axis scrollbar. * The minimum range scale should be greater than zero. */ get minScale() { return this._minScale; } set minScale(value) { if (value > 0 && value != this._minScale) { this._minScale = core.asNumber(value); if (!this._rangeSlider) { return; } this._rangeSlider.minScale = value; } } /** * Removes the range selector from the chart. */ remove() { if (this._slbarContainer) { this._chart.hostElement.removeChild(this._slbarContainer); this._switchEvent(false); this._slbarContainer = null; } } _createScrollbar() { var chart = this._chart, chartHostEle = chart.hostElement; this._slbarContainer = core.createElement('<div class="wj-axis-scrollbar-container"></div>'); this._rangeSlider = new interaction._RangeSlider(this._slbarContainer, true, // has space click function true, // has dec and inc buttons { buttonsVisible: this._buttonsVisible, isHorizontal: this._isXAxis, isVisible: this._isVisible, minScale: this._minScale, seamless: true }); chartHostEle.appendChild(this._slbarContainer); this._switchEvent(true); } _switchEvent(isOn) { var eventListener = isOn ? 'addEventListener' : 'removeEventListener', eventHandler = isOn ? 'addHandler' : 'removeHandler'; if (this._chart.hostElement) { this._chart.rendered[eventHandler](this._refresh, this); this._rangeSlider.rangeChanged[eventHandler](this._updateAxisRange, this); this._rangeSlider.rangeChanging[eventHandler](this._updatingAxisRange, this); } } _refresh() { var chartHostEle = this._chart.hostElement, rangeSlider = this._rangeSlider, pa, pOffset, plotBox, axisRect = this._axis._axrect, axisEle, axisOffset, isBottom, isLeft, rsWidth, rOffset = core.getElementRect(this._slbarContainer); if (rangeSlider._isSliding) { return; } //init the scrollbar range if (this._min === null) { this._min = core.isDate(this._axis.actualMin) ? this._axis.actualMin.valueOf() : this._axis.actualMin; } if (this._max === null) { this._max = core.isDate(this._axis.actualMax) ? this._axis.actualMax.valueOf() : this._axis.actualMax; } pa = chartHostEle.querySelector('.' + chart.FlexChart._CSS_PLOT_AREA); pOffset = core.getElementRect(pa); plotBox = pa.getBBox(pa); //TODO: get the axis element when has multiple axes axisEle = chartHostEle.querySelector(this._isXAxis ? '.wj-axis-x' : '.wj-axis-y'); axisOffset = core.getElementRect(axisEle); if (axisOffset.height === 0) { return; } if (this._isXAxis) { isBottom = this._axis.position === chart.Position.Bottom; rangeSlider._refresh({ left: plotBox.x, top: isBottom ? axisOffset.top + axisOffset.height - rOffset.top : axisOffset.top - rOffset.top - axisOffset.height, width: plotBox.width }); } else { isLeft = this._axis.position === chart.Position.Left; rsWidth = rangeSlider._handleWidth; rangeSlider._refresh({ left: isLeft ? axisOffset.left - rOffset.left - rsWidth : axisOffset.left - rOffset.left + pOffset.width + this._axis._axrect.width, top: pOffset.top - rOffset.top, height: plotBox.height }); } } _updateAxisRange() { var chart, axis, range, rangeSlider = this._rangeSlider; chart = this._chart; axis = this._axis; range = this._max - this._min; axis.min = this._min + rangeSlider._minPos * range; axis.max = this._min + rangeSlider._maxPos * range; chart.invalidate(); } _updatingAxisRange() { var self = this; this._chartRefreshDelay = window.setTimeout(function () { if (self._chartRefreshDelay) { clearTimeout(self._chartRefreshDelay); self._chartRefreshDelay = null; } self._updateAxisRange(); }, 200); } }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@grapecity/wijmo': 'npm:@grapecity/wijmo/index.js', '@grapecity/wijmo.input': 'npm:@grapecity/wijmo.input/index.js', '@grapecity/wijmo.styles': 'npm:@grapecity/wijmo.styles', '@grapecity/wijmo.cultures': 'npm:@grapecity/wijmo.cultures', '@grapecity/wijmo.chart': 'npm:@grapecity/wijmo.chart/index.js', '@grapecity/wijmo.chart.analytics': 'npm:@grapecity/wijmo.chart.analytics/index.js', '@grapecity/wijmo.chart.animation': 'npm:@grapecity/wijmo.chart.animation/index.js', '@grapecity/wijmo.chart.annotation': 'npm:@grapecity/wijmo.chart.annotation/index.js', '@grapecity/wijmo.chart.finance': 'npm:@grapecity/wijmo.chart.finance/index.js', '@grapecity/wijmo.chart.finance.analytics': 'npm:@grapecity/wijmo.chart.finance.analytics/index.js', '@grapecity/wijmo.chart.hierarchical': 'npm:@grapecity/wijmo.chart.hierarchical/index.js', '@grapecity/wijmo.chart.interaction': 'npm:@grapecity/wijmo.chart.interaction/index.js', '@grapecity/wijmo.chart.radar': 'npm:@grapecity/wijmo.chart.radar/index.js', '@grapecity/wijmo.chart.render': 'npm:@grapecity/wijmo.chart.render/index.js', '@grapecity/wijmo.chart.webgl': 'npm:@grapecity/wijmo.chart.webgl/index.js', '@grapecity/wijmo.gauge': 'npm:@grapecity/wijmo.gauge/index.js', '@grapecity/wijmo.grid': 'npm:@grapecity/wijmo.grid/index.js', '@grapecity/wijmo.grid.detail': 'npm:@grapecity/wijmo.grid.detail/index.js', '@grapecity/wijmo.grid.filter': 'npm:@grapecity/wijmo.grid.filter/index.js', '@grapecity/wijmo.grid.search': 'npm:@grapecity/wijmo.grid.search/index.js', '@grapecity/wijmo.grid.grouppanel': 'npm:@grapecity/wijmo.grid.grouppanel/index.js', '@grapecity/wijmo.grid.multirow': 'npm:@grapecity/wijmo.grid.multirow/index.js', '@grapecity/wijmo.grid.transposed': 'npm:@grapecity/wijmo.grid.transposed/index.js', '@grapecity/wijmo.grid.transposedmultirow': 'npm:@grapecity/wijmo.grid.transposedmultirow/index.js', '@grapecity/wijmo.grid.pdf': 'npm:@grapecity/wijmo.grid.pdf/index.js', '@grapecity/wijmo.grid.sheet': 'npm:@grapecity/wijmo.grid.sheet/index.js', '@grapecity/wijmo.grid.xlsx': 'npm:@grapecity/wijmo.grid.xlsx/index.js', '@grapecity/wijmo.grid.selector': 'npm:@grapecity/wijmo.grid.selector/index.js', '@grapecity/wijmo.grid.cellmaker': 'npm:@grapecity/wijmo.grid.cellmaker/index.js', '@grapecity/wijmo.nav': 'npm:@grapecity/wijmo.nav/index.js', '@grapecity/wijmo.odata': 'npm:@grapecity/wijmo.odata/index.js', '@grapecity/wijmo.olap': 'npm:@grapecity/wijmo.olap/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.pdf.security': 'npm:@grapecity/wijmo.pdf.security/index.js', '@grapecity/wijmo.viewer': 'npm:@grapecity/wijmo.viewer/index.js', '@grapecity/wijmo.xlsx': 'npm:@grapecity/wijmo.xlsx/index.js', '@grapecity/wijmo.undo': 'npm:@grapecity/wijmo.undo/index.js', '@grapecity/wijmo.interop.grid': 'npm:@grapecity/wijmo.interop.grid/index.js', '@grapecity/wijmo.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', '@grapecity/wijmo.barcode': 'npm:@grapecity/wijmo.barcode/index.js', '@grapecity/wijmo.barcode.common': 'npm:@grapecity/wijmo.barcode.common/index.js', '@grapecity/wijmo.barcode.composite': 'npm:@grapecity/wijmo.barcode.composite/index.js', '@grapecity/wijmo.barcode.specialized': 'npm:@grapecity/wijmo.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);