将图例添加到自制条形图中

时间:2016-09-12 21:50:20

标签: javascript jquery css jquery-plugins

我知道你可能想知道自制的条形图?为什么不是现有的图书馆?好吧,我讨厌使用20000行代码的文件,而只需要500行。

哦,这很有趣:)主要目标是我将使用这个脚本为我将使用Phonegap制作的应用程序。所以尺寸越小越好。

所以我们的想法是实现以下目标: enter image description here

我已经能够绘制条形图,确保它们具有相等的宽度并且其高度取决于父容器的高度。正如您在下面的代码中看到的,我还为选项添加了字体大小。因为某些图表将扩展到300px的高度(例如,使用默认的16px)。有些只有50像素,字体大小为12或更小。因此,我将ChartContainer栏缩小了3 x字形大小(+ rest),以确保有足够的空间用于顶部(金额)和&底部(传说+标题)

现在我不完全确定如何添加和居中金额。我尝试搜索现有的图表库以检查它是如何呈现的,不幸的是它们都使用了画布或SVG容器。有什么建议吗?

/* 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>

2 个答案:

答案 0 :(得分:2)

好的图表!它看起来很干净。我明白你的意思。我花了几个月试图操纵bxSlider的布局然后我意识到编写自己的代码更少。这是尝试回答您的查询。我已经通过使用百分比使其宽度响应(不用担心;它很容易更改),为图例,值和计数添加了额外的css类,并将插件修改为包括你的名字选项(称为图例)。然后,这些位被格式化并附加。希望这可以帮助。

&#13;
&#13;
/* 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 ));
&#13;
/* 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%;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

竖起大拇指以避免不必要的代码量 - 干净的条形图是不需要大量库的完美示例。

如果您使用<table>作为基本结构来创建条形图,则可以轻松访问所需的格式选项,从而减少代码:

  • 值为一行,标签为一行
  • 可以通过为每个值单元格的border-bottom设置样式来制作条形图
  • 文本可以在表格单元格中居中
  • 为图例添加<caption>,默认情况下居中,并且可以轻松放置在具有caption-side属性的表格下方
  • 设置值单元格的vertical-align属性样式允许将值直接定位在条形图上方(如下面的演示中)或排列在顶部(如图所示) - 这是在第20行中控制的下面的js演示代码。

毕竟,条形图只是对表格数据的虚拟化,所以使用表格是有道理的。

一个工作演示(大约30行vanilla js和几行css,如果需要,你可以很容易地适应你的jquery方法):

function barchart(containerId, options) {
  var i, 
      html,
      valueRow = '', 
      labelRow = '',
      data = options.dataset,
      maxBarHeight = 60, /* in px, could be set from options */
      barWidth = 20,     /* in px, could be set from options */
      maxValue = Math.max.apply(
        Math,
        data.map(function(o) {
          return o.a;
        })
      );
  for(i = 0; i < data.length; i++){
    labelRow += '<td>' + data[i].label + '</td>';
    valueRow += '<td style="border-bottom:' +
        (data[i].a * maxBarHeight / maxValue) +
        'px solid black;vertical-align:' +
        'bottom' + /* set to 'top' to get value labels lined up */
        ';width: ' +
        barWidth + 'px">' +
        data[i].a + '</td>';
  }
  html = '<table class="barchart" ' + 
    'style="font-size:' + options.fontSize + 'px">' +
    '<caption>' + options.name + '</caption>' + 
    '<tr>' + valueRow + '</tr>' + 
    '<tr>' + labelRow + '</tr>' + 
    '</table>';
  document.getElementById(containerId)
    .innerHTML = html;
}

/* create a barchart */

barchart('testdiv', {
  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'}
  ]
});
.barchart td{
  text-align: center;
}

.barchart{
  font-family: 'arial narrow', sans-serif;
  caption-side: bottom;
}
<div id="testdiv"></div>