plotters

0.9.19 • Public • Published

plotters

NPM version Build Status Clean Code Dependency Status devDependency Status License

Screenshot

Browser support

Chrome, Safari, Firefox, Opera, IE9+

Installation

    npm install plotters
 
    or 
 
    jspm install npm:plotters

Usage

This is a full stock chart:

A full chart

It has necessary functionalities for a stock chart. It contains a CANVAS plotter, timeline axis, number axis, timeline zoom, chart grid, etc. The CANVAS plotter is the key of a chart. Plotters class is such a component.

This example contains a simple chart implementation which can generate canvas graphs similiar to the first screenshot.

  • This example is based on the demo, you can run the demo to see how it works.
  • If you like you can read demo.js directly, the code is clean.

An example (pretty long) :

 
    /**
    * Load needed modules.
    */
    var $         = require('jquery'),
        Arrays    = require('array-es5'),
        Dates     = require('date-es5'),
        DateIndex = require('date-index'),
        Plotters  = require('./lib/plotters.js');
 
 
    /*
    * A simple Chart. 
    * We wrap plotter functionality in a Class called Chart.
    * 
    * 1. plotterStyle() method will create a plotter instance and set plotting style. 
    * 2. load() method will load data and draw graphs on CANVAS.
    */
    var Chart = function (domParent) {
        this._parent = domParent;
        this._buildUI();
    };
 
    Chart.prototype = {
        constructor: Chart,
 
        _colors: {
            yellow  : '#b58900',
            orange  : '#cb4b16',
            red     : '#dc322f',
            magenta : '#d33682',
            violet  : '#6c71c4',
            blue    : '#268bd2',
            cyan    : '#2aa198',
            green   : '#859900'
        },
 
        _regexPlotterStyle: /^(Line|Bar|OHLC)(?:\?(\w+))?$/,
 
        _buildUI: function () {
            var container = $('<div>').appendTo(this._parent)
                                      .addClass('chart');
            this._canvas = $('<canvas>').appendTo(container);
            this._canvas[0].width  = 360;
            this._canvas[0].height = 280;
            this._header = $('<p>').appendTo(container);
 
            this._xAxis = new DateAxis();
            this._yAxis = new NumberAxis();
        },
 
        plotter: function () {
            return this._plotter;
        },
 
        _getPlotterStyle: function (className, style) {
            return Plotters[className].Style[style];
        },
 
        plotterStyle: function (plotterStyle, hdrText) {
            var match = this._regexPlotterStyle.exec(plotterStyle);
            if (match === null)
                throw "IllegalArgumentException: plotterStyle is not supported: " + plotterStyle;
 
            var className = match[1],
                style     = match[2];
 
            /**
            * Create plotter instance.
            */
            this._plotter = new Plotters[className]();
            if (   typeof style === 'string'
                && style !== '' )
                /**
                * Set plotter style.
                */
                this._plotter.style(this._getPlotterStyle(className, style));
            else 
                style = '';
 
            this._plotter.canvas(this._canvas[0]);  // Set CANVAS object.
 
            if (typeof hdrText === 'undefined') {
                hdrText = className;
                if (style.length > 0)
                    hdrText += '' + style;
            }
            this._header.text(hdrText);
 
            return this;
        },
 
        setColor: function (color) {
            this._plotter.color(this._colors[color]);   // Set plotter color.
            return this;
        },
 
        _getInfo: function (data) {
            var dates  = [],
                min    = Infinity,
                max    = -Infinity,
                len    = data.length;
 
            for (var i = 0; i < len; i++) {
                var row = data[i];
 
                dates.push(row[0]);
                
                for (var j = 1; j < row.length; j++) {
                    var val = row[j];
 
                    if (min > val)
                        min = val;
                    if (max < val)
                        max = val;
                }
            }
 
            return {
                dates: dates,
                min: min,
                max: max
            };
        },
 
        load: function (data) {
            var len     = data.length,
                info    = this._getInfo(data),
                plotter = this._plotter;
 
            this._xAxis.setDates(info.dates);           // Set dates info.
            this._yAxis.setRange(info.min, info.max);   // Set value info.
 
            /**
            * Preparation.
            */
            plotter.open({
                xAxis                : this._xAxis,     // `xAxis` must have 'getPosition' method.
                yAxis                : this._yAxis,     // `yAxis` must have 'getPosition' method.
                numberOfPoints       : len,             // Number of data points.
                numberOfTicks        : len,             
                numberOfTicksEstimate: len,
                /**
                * msMeasure: dates' measure in milliseconds.
                * ( Since dates in `data` are something like `2016-05-01`, `2016-05-02`, 
                *  `2016-05-03`, so the measure (gap) is 1 day. )
                */
                msMeasure            : Dates.millisPerDay,      
                recordSize           : 1
            });
            
            for (var i = 0; i < len; i++) {
                var row = data[i];
 
                /**
                * put() method add data point to plotter instance.
                */
                plotter.put(row.shift(), row.length > 1 ? row : row[0]);
            }
 
            /**
            * Plotter instance draw CANVAS by calling close() method.
            * this._xAxis, this._yAxis both need to implement getPosition() method,
            * Plotter instance will call each one's getPosition() to convert date and 
            * value to corresponding x and y coordinates on the canvas.
            */
            plotter.close();
        }
    };
 
 
    /**
    * A simplified DateAxis class.
    */
    var DateAxis = function () {
        this._index = new DateIndex();
    };
    DateAxis.prototype = {
        constructor: DateAxis,
 
        setDates: function (dates) {
            /**
            * Index dates.
            */
            this._index.setAll(Arrays.iterator(dates));
 
            this._lowIdx  = this._index.get(dates[0]);
            this._highIdx = this._index.get(dates[dates.length - 1]);
        },
 
        /**
        * Get a date's relative position.
        */
        getPosition: function (date) {
            var idx = this._index.get(date);
            if (idx < 0)
                throw "IllegalArgumentException: date is not found.";
 
            return ( idx - this._lowIdx )
                 / ( this._highIdx - this._lowIdx );
        }
    };
 
    /**
    * A simplified NumberAxis class.
    */
    var NumberAxis = function () {
        this._min   = null;
        this._max   = null;
        this._range = null;
    };
    NumberAxis.prototype = {
        constructor: NumberAxis,
 
        setRange: function (min, max) {
            this._min   = min;
            this._max   = max;
            this._range = this._max - this._min;
        },
 
        /**
        * Get relative position of a value.
        */
        getPosition: function (number) {
            if (   typeof number !== 'number'
                || isNaN(number) )
                throw "IllegalArgumentException: number must be a Number.";
            
            return (number - this._min) / this._range;
        }
    };
 
 
    /**
    * You can ignore this object, its purpose is to generate a series of fake data.
    */
    var util = {
 
        _addDay: function (date, i) {
            return new Date(date.getTime() + i * Dates.millisPerDay);
        },
 
        /**
        * Generate an array of date and values according to given value range, 
        * base date, and number of data points.
        */
        generateData: function (baseDateStr, minPrice, maxPrice, numOfPoints, isOHLC) {
 
            var priceGen = this._priceGenerator(minPrice, maxPrice, numOfPoints),
                baseDate = Dates.isoStringToUTCDate(baseDateStr),
                data     = [];
 
            for (var i = 0; i < numOfPoints; i++) {
                var date  = this._addDay(baseDate, i),
                    price = priceGen(),
                    row   = [date, price];
 
                if (isOHLC) {
                    var close = price + Math.random() - 0.5;
                    row.push(this._toDollars(Math.max(close, price + (Math.random() / 4))));
                    row.push(this._toDollars(Math.min(close, price - (Math.random() / 4))));
                    row.push(this._toDollars(close));
                }
 
                data.push(row);
            }
 
            return data;
        },
 
        _priceGenerator: function (minPrice, maxPrice, length) {
            var range     = (maxPrice - minPrice) / 40;  // 5% variation
            var variation = range / 5;                  // point by point variation: 0.25%
            var barAt     = (maxPrice + minPrice) / 2;   // start in the middle.
 
            var inc     = 0;
            var cnt     = 0;
            
            return function () {
 
                if (--cnt <= 0) {
                    cnt = Math.ceil(Math.random() * 5);
                    inc = (Math.random() * range) - (range / 2);
                    if (   (barAt < minPrice + range && inc < 0)
                        || (barAt > maxPrice - range && inc > 0) )
                        inc = -inc;
                }
                barAt += inc;
                return util._toDollars((Math.random() * variation) + barAt);
            };
        },
 
        _toDollars: function (number) {
            return Math.round(number * 100) / 100;
        }
    };
 
 
    /*************************************************
    *********  Draw charts.
    **************************************************/
 
    var playGround = $('#play-ground');
 
    var reloadAllCharts = function () {
        playGround.html('');    // I am lazy.
        loadAllCharts();
    };
    $('#regen').bind('click', reloadAllCharts);
 
    var loadAllCharts = function () {
 
        var chart = new Chart(playGround),
            /**
            * Generate an array of data which contains 20 data-points, 
            * value ranges between 10 and 20.
            */
            data  = util.generateData('2016-05-01', 10, 20, 20, false);     
 
        /**
        * The format of generated `data` will be something like: 
        * 
        * [ [Date, 10], [Date, 13.5], [Date, 16.7], 0.9.19. ]
        */
 
        chart.plotterStyle('Line')  // Plotter Line.
             .setColor('yellow')
             .load(data);
 
        chart = new Chart(playGround);
        data  = util.generateData('2016-05-01', 10, 20, 20, false);
        chart.plotterStyle('Line?MOUNTAIN')     // Plotter Mountain.
             .setColor('orange')
             .load(data);
 
        chart = new Chart(playGround);
        data  = util.generateData('2016-05-01', 10, 20, 20, false);
        chart.plotterStyle('Line?MOUNTAIN', 'Line: (BaseValue is 15)');     
        var plotter = chart.plotter();
        plotter.baseline(15);   // Plotter MOUNTAIN with base value setting to 15.
        chart.setColor('red')
             .load(data);
 
        chart = new Chart(playGround);
        data  = util.generateData('2016-05-01', 10, 20, 20, false);
        chart.plotterStyle('Line?DASHED')   // Dashed Line
             .setColor('magenta');
 
        var plotter = chart.plotter();
        plotter.lineWidth(2)
               .isFirstSegmentSolid(true)
               .ratioSpaceToSolid(0.8)
               /**
               * Draw dashed line starts from '2016-05-07'
               */
               .dashedTransitions([Dates.isoStringToUTCDate('2016-05-07')]);    
        chart.load(data);
 
        chart = new Chart(playGround);
        data  = util.generateData('2016-05-01', 10, 20, 20, false);
        chart.plotterStyle('Bar?ABOVE_AND_BELOW')   // Bar.
             .setColor('violet')
             .load(data);
 
        chart = new Chart(playGround);
        data = util.generateData('2016-05-01', 10, 20, 20, true);
        chart.plotterStyle('OHLC')      // Stock OHLC.
             .setColor('blue')
             .load(data);
 
        chart = new Chart(playGround);
        data = util.generateData('2016-05-01', 10, 20, 20, true);
        chart.plotterStyle('OHLC?CANDLESTICKS')
             .setColor('cyan')
             .load(data);
 
        chart = new Chart(playGround);
        data = util.generateData('2016-05-01', 10, 20, 20, true);
        chart.plotterStyle('OHLC?CANDLESTICKS_RED_GREEN')
             .setColor('green')
             .load(data);
    };
 
    loadAllCharts();
 

Demo

  1. Clone this repo.
  2. Run npm install ( You can skip this step if you have a globally installed jspm.)
  3. Run jspm install.
  4. Run live-server ( live-server is very useful, but if you have other server tools you don't have to use it.)
  5. Open demo.html in a browser.

Package Sidebar

Install

npm i plotters

Weekly Downloads

10

Version

0.9.19

License

MIT

Last publish

Collaborators

  • wenwu