2015-08-21 4 views
0

У меня есть требование визуализации набора данных временных рядов смежных блоков.Ширина динамической полосы Chart.js

Мне нужно описать серию стержней, которые могут охватывать много часов или минут, со своим значением Y.

Я не уверен, что ChartJS - это то, что я должен использовать для этого, но я рассмотрел расширение типа Bar, но он кажется очень жестко закодированным для каждого бара равной ширине. Класс Scale внутри используется для меток, ширины диаграммы и т. Д., А не только для самих баров.

Я пытаюсь добиться чего-то вроде этого, который работает в Excel: http://peltiertech.com/variable-width-column-charts/

ли кто-то еще должен был придумать что-то подобное?

enter image description here

+0

http://stackoverflow.com/questions/13058195? Это highcharts btw (требуется лицензия, если она используется на коммерческой основе), а не chart.js – potatopeelings

+0

Спасибо, это хороший вариант, если я не могу найти версию, построенную на D3 (я хочу нарисовать другие элементы на панели). – James

+0

D3 - действительно хороший вариант. И если вы еще этого не видели - проверьте http://stackoverflow.com/questions/21610828 – potatopeelings

ответ

0

Для Chart.js вы можете создать новое расширение на основе класса бар, чтобы сделать это. Это немного участвует, хотя - однако большинство из них является копией пасты кода библиотеки типа бар

Chart.types.Bar.extend({ 
    name: "BarAlt", 
    // all blocks that don't have a comment are a direct copy paste of the Chart.js library code 
    initialize: function (data) { 

     // the sum of all widths 
     var widthSum = data.datasets[0].data2.reduce(function (a, b) { return a + b }, 0); 
     // cumulative sum of all preceding widths 
     var cumulativeSum = [ 0 ]; 
     data.datasets[0].data2.forEach(function (e, i, arr) { 
      cumulativeSum.push(cumulativeSum[i] + e); 
     }) 


     var options = this.options; 

     // completely rewrite this class to calculate the x position and bar width's based on data2 
     this.ScaleClass = Chart.Scale.extend({ 
      offsetGridLines: true, 
      calculateBarX: function (barIndex) { 
       var xSpan = this.width - this.xScalePaddingLeft; 
       var x = this.xScalePaddingLeft + (cumulativeSum[barIndex]/widthSum * xSpan) - this.calculateBarWidth(barIndex)/2; 
       return x + this.calculateBarWidth(barIndex); 
      }, 
      calculateBarWidth: function (index) { 
       var xSpan = this.width - this.xScalePaddingLeft; 
       return (xSpan * data.datasets[0].data2[index]/widthSum); 
      } 
     }); 

     this.datasets = []; 

     if (this.options.showTooltips) { 
      Chart.helpers.bindEvents(this, this.options.tooltipEvents, function (evt) { 
       var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : []; 

       this.eachBars(function (bar) { 
        bar.restore(['fillColor', 'strokeColor']); 
       }); 
       Chart.helpers.each(activeBars, function (activeBar) { 
        activeBar.fillColor = activeBar.highlightFill; 
        activeBar.strokeColor = activeBar.highlightStroke; 
       }); 
       this.showTooltip(activeBars); 
      }); 
     } 

     this.BarClass = Chart.Rectangle.extend({ 
      strokeWidth: this.options.barStrokeWidth, 
      showStroke: this.options.barShowStroke, 
      ctx: this.chart.ctx 
     }); 

     Chart.helpers.each(data.datasets, function (dataset, datasetIndex) { 

      var datasetObject = { 
       label: dataset.label || null, 
       fillColor: dataset.fillColor, 
       strokeColor: dataset.strokeColor, 
       bars: [] 
      }; 

      this.datasets.push(datasetObject); 

      Chart.helpers.each(dataset.data, function (dataPoint, index) { 
       datasetObject.bars.push(new this.BarClass({ 
        value: dataPoint, 
        label: data.labels[index], 
        datasetLabel: dataset.label, 
        strokeColor: dataset.strokeColor, 
        fillColor: dataset.fillColor, 
        highlightFill: dataset.highlightFill || dataset.fillColor, 
        highlightStroke: dataset.highlightStroke || dataset.strokeColor 
       })); 
      }, this); 

     }, this); 

     this.buildScale(data.labels); 
     // remove the labels - they won't be positioned correctly anyway 
     this.scale.xLabels.forEach(function (e, i, arr) { 
      arr[i] = ''; 
     }) 

     this.BarClass.prototype.base = this.scale.endPoint; 

     this.eachBars(function (bar, index, datasetIndex) { 
      // change the way the x and width functions are called 
      Chart.helpers.extend(bar, { 
       width: this.scale.calculateBarWidth(index), 
       x: this.scale.calculateBarX(index), 
       y: this.scale.endPoint 
      }); 

      bar.save(); 
     }, this); 

     this.render(); 
    }, 
    draw: function (ease) { 
     var easingDecimal = ease || 1; 
     this.clear(); 

     var ctx = this.chart.ctx; 

     this.scale.draw(1); 

     Chart.helpers.each(this.datasets, function (dataset, datasetIndex) { 
      Chart.helpers.each(dataset.bars, function (bar, index) { 
       if (bar.hasValue()) { 
        bar.base = this.scale.endPoint; 
        // change the way the x and width functions are called 
        bar.transition({ 
         x: this.scale.calculateBarX(index), 
         y: this.scale.calculateY(bar.value), 
         width: this.scale.calculateBarWidth(index) 
        }, easingDecimal).draw(); 

       } 
      }, this); 

     }, this); 
    } 
}); 

Вы передаете в ширину, как показано ниже

var data = { 
    labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'], 
    datasets: [ 
     { 
      label: "My First dataset", 
      fillColor: "rgba(220,220,220,0.5)", 
      strokeColor: "rgba(220,220,220,0.8)", 
      highlightFill: "rgba(220,220,220,0.7)", 
      highlightStroke: "rgba(220,220,220,1)", 
      data: [65, 59, 80, 30, 56, 65, 40], 
      data2: [10, 20, 30, 20, 10, 40, 10] 
     }, 
    ] 
}; 

и вы называете это как так

var ctx = document.getElementById('canvas').getContext('2d'); 
var myLineChart = new Chart(ctx).BarAlt(data); 

Fiddle - http://jsfiddle.net/moye0cp4/


enter image description here

0

я обнаружил, что нужно сделать, это и ответ на @potatopeelings был велик, но из даты для версии 2 Chartjs. Я сделал что-то подобное, создавая свой собственный тип контроллера/диаграммы с помощью простирающегося бара:

//controller.barw.js 

module.exports = function(Chart) { 

    var helpers = Chart.helpers; 

    Chart.defaults.barw = { 
     hover: { 
      mode: 'label' 
     }, 

     scales: { 
      xAxes: [{ 
       type: 'category', 

       // Specific to Bar Controller 
       categoryPercentage: 0.8, 
       barPercentage: 0.9, 

       // grid line settings 
       gridLines: { 
        offsetGridLines: true 
       } 
      }], 
      yAxes: [{ 
       type: 'linear' 
      }] 
     } 
    }; 

    Chart.controllers.barw = Chart.controllers.bar.extend({ 

     /** 
     * @private 
     */ 
     getRuler: function() { 
      var me = this; 
      var scale = me.getIndexScale(); 
      var options = scale.options; 
      var stackCount = me.getStackCount(); 
      var fullSize = scale.isHorizontal()? scale.width : scale.height; 
      var tickSize = fullSize/scale.ticks.length; 
      var categorySize = tickSize * options.categoryPercentage; 
      var fullBarSize = categorySize/stackCount; 
      var barSize = fullBarSize * options.barPercentage; 

      barSize = Math.min(
       helpers.getValueOrDefault(options.barThickness, barSize), 
       helpers.getValueOrDefault(options.maxBarThickness, Infinity)); 

      return { 
       fullSize: fullSize, 
       stackCount: stackCount, 
       tickSize: tickSize, 
       categorySize: categorySize, 
       categorySpacing: tickSize - categorySize, 
       fullBarSize: fullBarSize, 
       barSize: barSize, 
       barSpacing: fullBarSize - barSize, 
       scale: scale 
      }; 
     }, 


     /** 
     * @private 
     */ 
     calculateBarIndexPixels: function(datasetIndex, index, ruler) { 
      var me = this; 
      var scale = ruler.scale; 
      var options = scale.options; 
      var isCombo = me.chart.isCombo; 
      var stackIndex = me.getStackIndex(datasetIndex); 
      var base = scale.getPixelForValue(null, index, datasetIndex, isCombo); 
      var size = ruler.barSize; 

      var dataset = me.chart.data.datasets[datasetIndex]; 
      if(dataset.weights) { 
       var total = dataset.weights.reduce((m, x) => m + x, 0); 
       var perc = dataset.weights[index]/total; 
       var offset = 0; 
       for(var i = 0; i < index; i++) { 
        offset += dataset.weights[i]/total; 
       } 
       var pixelOffset = Math.round(ruler.fullSize * offset); 
       var base = scale.isHorizontal() ? scale.left : scale.top; 
       base += pixelOffset; 

       size = Math.round(ruler.fullSize * perc); 
       size -= ruler.categorySpacing; 
       size -= ruler.barSpacing; 
      }    

      base -= isCombo? ruler.tickSize/2 : 0; 
      base += ruler.fullBarSize * stackIndex; 
      base += ruler.categorySpacing/2; 
      base += ruler.barSpacing/2; 

      return { 
       size: size, 
       base: base, 
       head: base + size, 
       center: base + size/2 
      }; 
     }, 
    }); 
}; 

Затем вам нужно добавить его к экземпляру chartjs так:

import Chart from 'chart.js' 
import barw from 'controller.barw' 

barw(Chart); //add plugin to chartjs 

и, наконец, похожего на другой ответ, вес бруска ширины должен быть добавлен к набору данных:

var data = { 
    labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'], 
    datasets: [ 
     { 
      label: "My First dataset", 
      fillColor: "rgba(220,220,220,0.5)", 
      strokeColor: "rgba(220,220,220,0.8)", 
      highlightFill: "rgba(220,220,220,0.7)", 
      highlightStroke: "rgba(220,220,220,1)", 
      data: [65, 59, 80, 30, 56, 65, 40], 
      weights: [1, 0.9, 1, 2, 1, 4, 0.3] 
     }, 
    ] 
}; 

будет надеяться, заставить кого-то на правильном пути. То, что я, безусловно, не совершенен, но если вы убедитесь, что у вас есть правильное количество весовых точек данных, вы должны быть правы.

Удачи.

Смежные вопросы