0.9.19 • Published 9 years ago

plotters v0.9.19

Weekly downloads
22
License
MIT
Repository
github
Last release
9 years ago

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.
0.9.19

9 years ago

0.9.18

10 years ago

0.9.17

10 years ago

0.9.16

10 years ago

0.9.15

10 years ago

0.9.14

10 years ago

0.9.12

10 years ago

0.9.11

10 years ago

0.9.10

10 years ago

0.9.9

10 years ago

0.9.8

10 years ago

0.9.7

10 years ago

0.9.5

10 years ago

0.9.4

10 years ago

0.9.1

10 years ago

0.9.0

10 years ago