Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding legends to selfmade barchart

Tags:

I know you are probably wondering self-made barchart? Why not an existing library? Well I hate to use files with 20000 lines of code while only 500 are necessary.

Oh and it's fun :) The main objective is that I'll be using this script for an app I'll be making using Phonegap. So the lower the size, the better.

So the idea is to achieve the following: enter image description here

I've been able to to draw the bars, make sure they are of equal width and have their height dependent on the height of the parent container. As you'll see in the code below I also added a font-size to the options. As some chart will expand around 300px of height (which would be using the default 16px for example). And some only 50px with a font-size of 12 or less. So I reduced the bar ChartContainer by 3 x the fontsize (+ rest) to make sure there is enough space for the top (amounts) & bottom (legends + title)

Now I'm not entirely sure how to add and center the amounts. I tried searching existing chart libraries to check on how it all has been rendered, unfortunately they all use canvasses or SVG containers. Any suggestions?

/* dataset
   ------------------

   add legends
   add result / amount
   add bottom-border: 8px extra to both sides?
   add chart name

 */

(function ($) {

    var methods = {
        init : function(options) {
            return this.each(function() {

                var $this = $(this),
                    dataset = options.dataset,
                    fontSize = options.fontSize,
                    widthOfContainer = $this.width(),
                    heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
                    widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
                    bar;

                $this.bind('draw', function(e) {
                    $this.empty();

                    var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
                        heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
                    for (var i = 0; i < dataset.length; i++) {
                        bar = $(document.createElement('div'));
                        bar.addClass('bar');
                        bar.css({
                            'height': parseInt(dataset[i].a * heightPerUnit) + 'px',
                            'width': parseInt(widthOfBar) + 'px',
                            'margin-left': parseInt(i * 2 + i * widthOfBar) + 'px',
                            'bottom': 2 * (fontSize + 4)
                        });
                        $this.append(bar);
                    }
                });

                $this.trigger('draw');
            });
        },
        draw : function(n) {
            $(this).trigger('draw');
        }
    };

    $.fn.chart = function(methodOrOptions) {
        if ( methods[methodOrOptions] ) {
            return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
            // Default to "init"
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  methodOrOptions + ' does not exist on jQuery.tooltip' );
        }
    };

    $(document).ready(function(){
        $('div.barchart').chart({
            // Add font-size?
            fontSize: 14,
            name: 'mana cost',
            dataset: [
                {a: 2, label: '0'},
                {a: 8, label: '1'},
                {a: 9, label: '2'},
                {a: 4, label: '3'},
                {a: 7, label: '4'},
                {a: 3, label: '5'},
                {a: 1, label: '6'},
                {a: 1, label: '7'},
                {a: 2, label: '8'},
                {a: 5, label: '9'}
            ]
        });
    });
}( jQuery ));
/* Barchart
   ========================================================================== */

.barchart {
  color: black;
}

/* Bar
   ========================================================================== */

.bar {
  position: absolute;
  height: 0px;
  width: 0px;
  margin-left: 0px;
  bottom: 0px;
  background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
            <div class="barchart" style="height: 100px; position: relative"></div>
        </div>
like image 866
Goowik Avatar asked Sep 12 '16 21:09

Goowik


1 Answers

Nice chart! It looks clean. I know what you mean. I spent months trying to manipulate the layout of bxSlider then I realized it was less code to write my own. Here's an attempt at answering your query. I've made it width responsive by using percentages (don't worry; it's easy to change back), added an extra css class for the legend, values, and count, and modified your plugin to include your name option (called legend). These bits are then just formatted and appended. Hope this helps.

/* dataset
   ------------------

   add legends
   add result / amount
   add bottom-border: 8px extra to both sides?
   add chart name

 */

(function ($) {

    var methods = {
        init : function(options) {
            return this.each(function() {

                var $this = $(this),
                    dataset = options.dataset,
                    fontSize = options.fontSize,
                    legend = options.name,
                    widthOfContainer = $this.width(),
                    heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
                    widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
                    widthOfBarPer = (widthOfBar / widthOfContainer) *100,
                    bar;

                $this.bind('draw', function(e) {
                    $this.empty();

                    var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
                        heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
                    for (var i = 0; i < dataset.length; i++) {
                    		var dataVal = dataset[i].a;
                        bar = $(document.createElement('div'));
                        bar.addClass('bar');
                        bar.css({
                            'height': parseInt( dataVal * heightPerUnit) + 'px',
                            'width': widthOfBarPer + '%', // percentages to make more responsive?
                            'margin-left': (i + i * widthOfBarPer ) + '%', // no need to parseInt as you have already on widthOfBar. now your chart stretches with the width .
                            'bottom': 2 * (fontSize + 4)
                        });
                        bar.append('<p class="count">'+ i +'</p>');	// defines the bar count, this could be dataset[i].label but if you just use i then you don't need to type it out for each bar?
                        bar.append('<p class="value">'+ dataVal +'</p>');	// defines the bar value
                        $this.append(bar); // adds the bar count
                    }
                    var chartHeight = $this.height();
                    $('.bar .count').css({ bottom: fontSize - chartHeight * 0.5 });
                    $('.bar .value').css({ bottom: chartHeight * 0.5 - fontSize });
                    if(legend){
												legend = '<p class="legend">'+legend+'</p>';
											  $this.append(legend);
                  //      $this.css({ border: '1px solid #f90'}); // temp to see the current chart size
                        $this.find('.legend').css({ top: chartHeight - fontSize });
                    }
                });

                $this.trigger('draw');
            });
        },
        draw : function(n) {
            $(this).trigger('draw');
        }
    };

    $.fn.chart = function(methodOrOptions) {
        if ( methods[methodOrOptions] ) {
            return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
            // Default to "init"
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  methodOrOptions + ' does not exist on jQuery.tooltip' );
        }
    };

    $(document).ready(function(){
        $('div.barchart').chart({
            // Add font-size?
            fontSize: 14,
            name: 'mana cost',
            dataset: [
                {a: 2, label: '0'},
                {a: 8, label: '1'},
                {a: 9, label: '2'},
                {a: 4, label: '3'},
                {a: 7, label: '4'},
                {a: 3, label: '5'},
                {a: 1, label: '6'},
                {a: 1, label: '7'},
                {a: 2, label: '8'},
                {a: 5, label: '9'}
            ]
        });
    });
}( jQuery ));
/* Barchart
   ========================================================================== */

.barchart {
  color: black;
}

/* Bar
   ========================================================================== */

.bar {
  position: absolute;
  height: 0px;
  width: 0px;
  margin-left: 0px;
  bottom: 0px;
  background: black;
}

.count, .value{
  z-index: 7;
  position: absolute;
  text-align: center;
  width: 100%;
}

.legend{
  position: relative;
  text-align: center;
  width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
            <div class="barchart" style="height: 100px; position: relative"></div>
        </div>
like image 125
Sam0 Avatar answered Oct 03 '22 19:10

Sam0