npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

plotters

v0.9.19

Published

A CANVAS plotter Class supports drawing Line, Mountain, Bar, Dashed-line, Stock OHLC.

Downloads

4

Readme

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.