web-dev-qa-db-fra.com

Comment créer des barres arrondies pour Bar Chart.js v2?

Essayer de arrondir les barres sur le graphique à barres comme on le trouve dans cet article qui fonctionne comme indiqué dans le jsFiddle fourni. C'est pour la version 1.

Dans le graphique que j'utilise, il ne parvient pas à se charger pour la référence à extend dans Chart.types.Bar.extend plante le script.

Si j'utilise l'option par défaut, le graphique ne charge aucun problème. J'ai dû placer le Chart.types.Bar.extend à la fin pour que l'option par défaut se charge correctement. Exécutez et affichez ceci en plein écran.

J'ai essayé de l'implémenter avec ma version de Chart.js 2.4.0.

Rapports Chrome:

TypeError non capturé: impossible de lire la propriété 'extend' de chart.js non défini

Ce code ne fonctionnera même pas ici. Pourquoi cela se produit-il? Quelqu'un pourrait-il m'aider?.

Ce code fonctionne avec une ancienne version de Chart.js 1.0. Quelqu'un pourrait-il s'il vous plaît montrer comment cela pourrait fonctionner avec la version Chart.js 2.0? Je vous remercie.

$(document).ready(function(){

        var myBarChart1 = new Chart($('#appBarChart2_NoRound'), {
                type: 'bar',
                data: dataBar2,
                options: optionsBar
        });
  
  var ctx = $("#appBarChart2").getContext("2d");

        var myBarChart2 = new Chart(ctx).BarAlt(dataBarAlt2, {
                // 0 (flat) to 1 (more curvy)
                curvature: 1
        });
});

var dataBarAlt2 = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [
        {
            fillColor: "#1A9BFC",
            strokeColor: "#1A9BFC",
            data: [65, 59, 80, 81, 56, 55, 40],
        }
    ]
};

var dataBar2 = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [
        {
            label: "My First dataset",
            backgroundColor: '#1A9BFC',
            borderColor:'#1A9BFC',
            borderWidth: 1,
            data: [65, 59, 80, 81, 56, 55, 40],
        }
    ]
};

var optionsBar =
                {
        scales: {
            xAxes: [{
                stacked: true,
                                barThickness: 20,
                                gridLines:{
                                        display:false,
                                }
//                              barPercentage:0.5,
            }],
            yAxes: [{
                stacked: true,
                                
//                              barPercentage:0.5,
            }]
        },
                legend: {
                        display: false,
//                      position: 'left'
                 }
    };


Chart.types.Bar.extend({
    name: "BarAlt",
    initialize: function (data) {
        Chart.types.Bar.prototype.initialize.apply(this, arguments);

        if (this.options.curvature !== undefined && this.options.curvature <= 1) {
            var rectangleDraw = this.datasets[0].bars[0].draw;
            var self = this;
            var radius = this.datasets[0].bars[0].width * this.options.curvature * 0.5;

            // override the rectangle draw with ours
            this.datasets.forEach(function (dataset) {
                dataset.bars.forEach(function (bar) {
                    bar.draw = function () {
                        // draw the original bar a little down (so that our curve brings it to its original position)
                        var y = bar.y;
                        // the min is required so animation does not start from below the axes
                        bar.y = Math.min(bar.y + radius, self.scale.endPoint - 1);
                        // adjust the bar radius depending on how much of a curve we can draw
                        var barRadius = (bar.y - y);
                        rectangleDraw.apply(bar, arguments);

                        // draw a rounded rectangle on top
                        Chart.helpers.drawRoundedRectangle(self.chart.ctx, bar.x - bar.width / 2, bar.y - barRadius + 1, bar.width, bar.height, barRadius);
                        ctx.fill();

                        // restore the y value
                        bar.y = y;
                    }
                })
            })
        }
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<p>Bar Chart - Working</p>
<canvas id="appBarChart2_NoRound" height="100" >
</div>
<div>
<p>Rounded Bar Chart - Not Working</p>
<canvas id="appBarChart2" height="100" >
</div>
16
mcv

Le code que vous tentiez d'utiliser est en fait pour chart.js v1 et, comme vous l'avez découvert, ne fonctionne pas pour chart.js v2 (qui est presque une réécriture complète de chart.js).

Pour obtenir les mêmes résultats dans chart.js v2, vous devez étendre Chart.elements.Rectangle et écraser sa méthode draw pour peindre le sommet arrondi. Il existe déjà une méthode d'assistance chart.js qui dessine un rectangle arrondi (Chart.helpers.drawRoundedRectangle), nous allons donc le modifier légèrement et créer une nouvelle méthode d'assistance qui ne dessinera qu'un sommet arrondi (au lieu de tous les côtés).

// draws a rectangle with a rounded top
Chart.helpers.drawRoundedTopRectangle = function(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  ctx.moveTo(x + radius, y);
  // top right corner
  ctx.lineTo(x + width - radius, y);
  ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  // bottom right   corner
  ctx.lineTo(x + width, y + height);
  // bottom left corner
  ctx.lineTo(x, y + height);
  // top left   
  ctx.lineTo(x, y + radius);
  ctx.quadraticCurveTo(x, y, x + radius, y);
  ctx.closePath();
};

Chart.elements.RoundedTopRectangle = Chart.elements.Rectangle.extend({
  draw: function() {
    var ctx = this._chart.ctx;
    var vm = this._view;
    var left, right, top, bottom, signX, signY, borderSkipped;
    var borderWidth = vm.borderWidth;

    if (!vm.horizontal) {
      // bar
      left = vm.x - vm.width / 2;
      right = vm.x + vm.width / 2;
      top = vm.y;
      bottom = vm.base;
      signX = 1;
      signY = bottom > top? 1: -1;
      borderSkipped = vm.borderSkipped || 'bottom';
    } else {
      // horizontal bar
      left = vm.base;
      right = vm.x;
      top = vm.y - vm.height / 2;
      bottom = vm.y + vm.height / 2;
      signX = right > left? 1: -1;
      signY = 1;
      borderSkipped = vm.borderSkipped || 'left';
    }

    // Canvas doesn't allow us to stroke inside the width so we can
    // adjust the sizes to fit if we're setting a stroke on the line
    if (borderWidth) {
      // borderWidth shold be less than bar width and bar height.
      var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
      borderWidth = borderWidth > barSize? barSize: borderWidth;
      var halfStroke = borderWidth / 2;
      // Adjust borderWidth when bar top position is near vm.base(zero).
      var borderLeft = left + (borderSkipped !== 'left'? halfStroke * signX: 0);
      var borderRight = right + (borderSkipped !== 'right'? -halfStroke * signX: 0);
      var borderTop = top + (borderSkipped !== 'top'? halfStroke * signY: 0);
      var borderBottom = bottom + (borderSkipped !== 'bottom'? -halfStroke * signY: 0);
      // not become a vertical line?
      if (borderLeft !== borderRight) {
        top = borderTop;
        bottom = borderBottom;
      }
      // not become a horizontal line?
      if (borderTop !== borderBottom) {
        left = borderLeft;
        right = borderRight;
      }
    }

    // calculate the bar width and roundess
    var barWidth = Math.abs(left - right);
    var roundness = this._chart.config.options.barRoundness || 0.5;
    var radius = barWidth * roundness * 0.5;

    // keep track of the original top of the bar
    var prevTop = top;

    // move the top down so there is room to draw the rounded top
    top = prevTop + radius;
    var barRadius = top - prevTop;

    ctx.beginPath();
    ctx.fillStyle = vm.backgroundColor;
    ctx.strokeStyle = vm.borderColor;
    ctx.lineWidth = borderWidth;

    // draw the rounded top rectangle
    Chart.helpers.drawRoundedTopRectangle(ctx, left, (top - barRadius + 1), barWidth, bottom - prevTop, barRadius);

    ctx.fill();
    if (borderWidth) {
      ctx.stroke();
    }

    // restore the original top value so tooltips and scales still work
    top = prevTop;
  },
});

Ensuite, vous devrez également étendre le contrôleur de graphique à barres (Chart.controllers.bar) et écraser dataElementType pour utiliser le nouveau "rectangle arrondi" pour le graphique au lieu d'un rectangle normal.

Chart.defaults.roundedBar = Chart.helpers.clone(Chart.defaults.bar);

Chart.controllers.roundedBar = Chart.controllers.bar.extend({
  dataElementType: Chart.elements.RoundedTopRectangle
});

Enfin, nous allons modifier la configuration du graphique pour utiliser le nouveau type de graphique créé ci-dessus et ajouter une nouvelle propriété d'options appelée barRoundness pour contrôler la hauteur du sommet (0 est plat, 1 est un demi-cercle).

var ctx = document.getElementById("canvas").getContext("2d");
var myBar = new Chart(ctx, {
  type: 'roundedBar',
  data: {
    labels: ["Car", "Bike", "Walking"],
    datasets: [{
      label: 'Students',
      backgroundColor: chartColors.blue,
      data: [
        randomScalingFactor(), 
        randomScalingFactor(), 
        randomScalingFactor(), 
      ]
    }, {
      label: 'Teachers',
      backgroundColor: chartColors.red,
      data: [
        randomScalingFactor(), 
        randomScalingFactor(), 
        randomScalingFactor(), 
      ]
    }, {
      label: 'Visitors',
      backgroundColor: chartColors.green,
      data: [
        randomScalingFactor(), 
        randomScalingFactor(), 
        randomScalingFactor(), 
      ]
    }]
  },
  options: {
    responsive: true,
    barRoundness: 1,
    title: {
      display: true,
      text: "Chart.js - Bar Chart with Rounded Tops (drawRoundedTopRectangle Method)"
    },
  }
});

Vous pouvez voir un exemple de travail complet à ce codepen .

De plus, au cas où vous voudriez un aspect légèrement différent de "sommet arrondi", voici un autre codepen qui utilise une approche différente pour dessiner le haut (une seule courbe quadratique).

14
jordanwillis

Ce qui suit ne personnalise que le Chart.elements.Rectangle.prototype.draw - pas besoin de créer un type de graphique entièrement nouveau.

Autres avantages:

  • Il fonctionne bien avec les graphiques à barres verticales et horizontales
  • Il fonctionne bien avec les graphiques à barres verticales et horizontales empilés - il arrondit uniquement la dernière case de la série

Problème constaté: car cela arrondit uniquement la case du dernier ensemble de données, si la valeur du point de données dans le dernier ensemble de données est <l'un des points de données précédents , la zone supérieure visuelle ne sera pas arrondie. Cependant, si le dernier point de données est négatif et la valeur la plus basse, il arrondira cette case dans les coins inférieurs.

Crédit: le code d'origine appartient à https://github.com/uffo . Le code ci-dessous et le violon lié montrent l'augmentation des valeurs positives dans chaque jeu de données pour chaque pile, et modifie également certaines des options de rayon par défaut.

/**Customize the Rectangle.prototype draw method**/
Chart.elements.Rectangle.prototype.draw = function() {
  var ctx = this._chart.ctx;
  var vm = this._view;
  var left, right, top, bottom, signX, signY, borderSkipped, radius;
  var borderWidth = vm.borderWidth;

  // If radius is less than 0 or is large enough to cause drawing errors a max
  //      radius is imposed. If cornerRadius is not defined set it to 0.
  var cornerRadius = this._chart.config.options.cornerRadius;
  var fullCornerRadius = this._chart.config.options.fullCornerRadius;
  var stackedRounded = this._chart.config.options.stackedRounded;
  var typeOfChart = this._chart.config.type;

  if (cornerRadius < 0) {
    cornerRadius = 0;
  }
  if (typeof cornerRadius == 'undefined') {
    cornerRadius = 0;
  }
  if (typeof fullCornerRadius == 'undefined') {
    fullCornerRadius = false;
  }
  if (typeof stackedRounded == 'undefined') {
    stackedRounded = false;
  }

  if (!vm.horizontal) {
    // bar
    left = vm.x - vm.width / 2;
    right = vm.x + vm.width / 2;
    top = vm.y;
    bottom = vm.base;
    signX = 1;
    signY = bottom > top ? 1 : -1;
    borderSkipped = vm.borderSkipped || 'bottom';
  } else {
    // horizontal bar
    left = vm.base;
    right = vm.x;
    top = vm.y - vm.height / 2;
    bottom = vm.y + vm.height / 2;
    signX = right > left ? 1 : -1;
    signY = 1;
    borderSkipped = vm.borderSkipped || 'left';
  }

  // Canvas doesn't allow us to stroke inside the width so we can
  // adjust the sizes to fit if we're setting a stroke on the line
  if (borderWidth) {
    // borderWidth shold be less than bar width and bar height.
    var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
    borderWidth = borderWidth > barSize ? barSize : borderWidth;
    var halfStroke = borderWidth / 2;
    // Adjust borderWidth when bar top position is near vm.base(zero).
    var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
    var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
    var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
    var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
    // not become a vertical line?
    if (borderLeft !== borderRight) {
      top = borderTop;
      bottom = borderBottom;
    }
    // not become a horizontal line?
    if (borderTop !== borderBottom) {
      left = borderLeft;
      right = borderRight;
    }
  }

  ctx.beginPath();
  ctx.fillStyle = vm.backgroundColor;
  ctx.strokeStyle = vm.borderColor;
  ctx.lineWidth = borderWidth;

  // Corner points, from bottom-left to bottom-right clockwise
  // | 1 2 |
  // | 0 3 |
  var corners = [
    [left, bottom],
    [left, top],
    [right, top],
    [right, bottom]
  ];

  // Find first (starting) corner with fallback to 'bottom'
  var borders = ['bottom', 'left', 'top', 'right'];
  var startCorner = borders.indexOf(borderSkipped, 0);
  if (startCorner === -1) {
    startCorner = 0;
  }

  function cornerAt(index) {
    return corners[(startCorner + index) % 4];
  }

  // Draw rectangle from 'startCorner'
  var corner = cornerAt(0);
  ctx.moveTo(corner[0], corner[1]);


  var nextCornerId, nextCorner, width, height, x, y;
  for (var i = 1; i < 4; i++) {
    corner = cornerAt(i);
    nextCornerId = i + 1;
    if (nextCornerId == 4) {
      nextCornerId = 0
    }

    nextCorner = cornerAt(nextCornerId);

    width = corners[2][0] - corners[1][0];
    height = corners[0][1] - corners[1][1];
    x = corners[1][0];
    y = corners[1][1];

    var radius = cornerRadius;
    // Fix radius being too large
    if (radius > Math.abs(height) / 2) {
      radius = Math.floor(Math.abs(height) / 2);
    }
    if (radius > Math.abs(width) / 2) {
      radius = Math.floor(Math.abs(width) / 2);
    }

      var x_tl, x_tr, y_tl, y_tr, x_bl, x_br, y_bl, y_br;
      if (height < 0) {
        // Negative values in a standard bar chart
        x_tl = x;
        x_tr = x + width;
        y_tl = y + height;
        y_tr = y + height;

        x_bl = x;
        x_br = x + width;
        y_bl = y;
        y_br = y;

        // Draw
        ctx.moveTo(x_bl + radius, y_bl);

        ctx.lineTo(x_br - radius, y_br);

        // bottom right
        ctx.quadraticCurveTo(x_br, y_br, x_br, y_br - radius);


        ctx.lineTo(x_tr, y_tr + radius);

        // top right
        fullCornerRadius ? ctx.quadraticCurveTo(x_tr, y_tr, x_tr - radius, y_tr) : ctx.lineTo(x_tr, y_tr, x_tr - radius, y_tr);


        ctx.lineTo(x_tl + radius, y_tl);

        // top left
        fullCornerRadius ? ctx.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl + radius) : ctx.lineTo(x_tl, y_tl, x_tl, y_tl + radius);


        ctx.lineTo(x_bl, y_bl - radius);

        //  bottom left
        ctx.quadraticCurveTo(x_bl, y_bl, x_bl + radius, y_bl);

      } else if (width < 0) {
        // Negative values in a horizontal bar chart
        x_tl = x + width;
        x_tr = x;
        y_tl = y;
        y_tr = y;

        x_bl = x + width;
        x_br = x;
        y_bl = y + height;
        y_br = y + height;

        // Draw
        ctx.moveTo(x_bl + radius, y_bl);

        ctx.lineTo(x_br - radius, y_br);

        //  Bottom right corner
        fullCornerRadius ? ctx.quadraticCurveTo(x_br, y_br, x_br, y_br - radius) : ctx.lineTo(x_br, y_br, x_br, y_br - radius);

        ctx.lineTo(x_tr, y_tr + radius);

        // top right Corner
        fullCornerRadius ? ctx.quadraticCurveTo(x_tr, y_tr, x_tr - radius, y_tr) : ctx.lineTo(x_tr, y_tr, x_tr - radius, y_tr);

        ctx.lineTo(x_tl + radius, y_tl);

        // top left corner
        ctx.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl + radius);

        ctx.lineTo(x_bl, y_bl - radius);

        //  bttom left corner
        ctx.quadraticCurveTo(x_bl, y_bl, x_bl + radius, y_bl);

      } else {
      
          var lastVisible = 0;
        for (var findLast = 0, findLastTo = this._chart.data.datasets.length; findLast < findLastTo; findLast++) {
          if (!this._chart.getDatasetMeta(findLast).hidden) {
            lastVisible = findLast;
          }
        }
        var rounded = this._datasetIndex === lastVisible;

        if (rounded) {
        //Positive Value
          ctx.moveTo(x + radius, y);

          ctx.lineTo(x + width - radius, y);

          // top right
          ctx.quadraticCurveTo(x + width, y, x + width, y + radius);


          ctx.lineTo(x + width, y + height - radius);

          // bottom right
          if (fullCornerRadius || typeOfChart == 'horizontalBar')
            ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
          else
            ctx.lineTo(x + width, y + height, x + width - radius, y + height);


          ctx.lineTo(x + radius, y + height);

          // bottom left
          if (fullCornerRadius)
            ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
          else
            ctx.lineTo(x, y + height, x, y + height - radius);


          ctx.lineTo(x, y + radius);

          // top left
          if (fullCornerRadius || typeOfChart == 'bar')
            ctx.quadraticCurveTo(x, y, x + radius, y);
          else
            ctx.lineTo(x, y, x + radius, y);
        }else {
          ctx.moveTo(x, y);
          ctx.lineTo(x + width, y);
          ctx.lineTo(x + width, y + height);
          ctx.lineTo(x, y + height);
          ctx.lineTo(x, y);
        }
      }
    
  }

  ctx.fill();
  if (borderWidth) {
    ctx.stroke();
  }
};

/**Chart Data**/
var data = {
  labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
  datasets: [{
    label: 'data 0',
    data: [12, 19, 3, 5, 2, 3],
    backgroundColor: [
      'rgba(255, 99, 132, 1)',
      'rgba(54, 162, 235, 1)',
      'rgba(255, 206, 86, 1)',
      'rgba(75, 192, 192, 1)',
      'rgba(153, 102, 255, 1)',
      'rgba(255, 159, 64, 1)'
    ],
    borderWidth: 0
  }, {
    label: 'data 1',
    data: [20, 24, 10, 15, 12, 13],
    backgroundColor: [
      'rgba(255, 159, 64, 1)',
      'rgba(255, 99, 132, 1)',
      'rgba(255, 206, 86, 1)',

      'rgba(54, 162, 235, 1)',
      'rgba(153, 102, 255, 1)',
      'rgba(75, 192, 192, 1)'

    ],
    borderWidth: 0
  }, {
    label: 'data 2',
    data: [20, 30, 30, 20, 14, 20],
    backgroundColor: [
      'rgba(75, 192, 192, 1)',
      'rgba(255, 159, 64, 1)',
      'rgba(255, 99, 132, 1)',
      'rgba(255, 206, 86, 1)',

      'rgba(54, 162, 235, 1)',
      'rgba(153, 102, 255, 1)'


    ],
    borderWidth: 0
  }]
};

/**Chart Options - Radius options are here**/
var options = {
        //Border radius; Default: 0; If a negative value is passed, it will overwrite to 0;
  cornerRadius: 10, 
  //Default: false; if true, this would round all corners of final box;
  fullCornerRadius: false, 
  //Default: false; if true, this rounds each box in the stack instead of only final box;
  stackedRounded: false,
        elements: {
    point: {
      radius: 25,
      hoverRadius: 35,
      pointStyle: 'rectRounded',

    }
  },
  scales: {
    yAxes: [{
      ticks: {
        beginAtZero: true
      },
      stacked: true,
      radius: 25
    }],
    xAxes: [{
      ticks: {
        beginAtZero: true
      },
      stacked: true,

    }]
  }
};

/**Generate Chart**/
var ctxBar = document.getElementById("myChart");
var myBarChart = new Chart(ctxBar, {
  type: 'bar',
  data: data,
  options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.js"></script>
<canvas id="myChart" height="300" width="800"></canvas>

Violon: https://jsfiddle.net/adammoisa/v0dthnyr/7/

4
Adam Moisa

Il convient également de mentionner cette solution qui vous prend 30 secondes pour mettre en œuvre:

https://github.com/jedtrow/Chart.js-Rounded-Bar-Charts

Téléchargez et placez le fichier .js dans votre dossier de projets, chargez-le et utilisez
var options = { cornerRadius: 20, };

pour obtenir des barres arrondies.

Crédits: https://github.com/jedtrow

0
Phanti

Commander chartjs-top-round-bar un utile

Vous avez juste besoin de

import 'chartjs-top-round-bar';

...

new Chart('myChart', 
{ 
    options: { 
        barRoundness: 0.3
    }
}
0
SimoneMSR