2

So I've been trying to understand the documentation for Chart.js but it's not actually friendly for new people to understand how you can customize certain stuff.

I have installed the plugin but it's not customizable as I want to have it, I tried using CSS but since it's a canvas you can't really do much.

I'm trying to achieve this design and add data from my mysql database and display it on the chart, I can understand how to do the rest apart from the designing the chart.

What I'm trying to achieve.

Chart example

Tweath
  • 124
  • 6
  • 24
  • What you are looking for is a bar chart. Here is an example: https://jsfiddle.net/red_stapler/Luaf2tm4/ try and get this going first, and then come back with a specific problem. Right now your question is too broad and we aren't here to write code for you. – Joseph Cho Nov 12 '18 at 18:51
  • I'm not here looking to get spoonfed code, I'm just simply asking for guidelines or pointer in the right direction, I've already managed to display my chart with the data from the mysql. – Tweath Nov 12 '18 at 18:52

2 Answers2

2

I think that the closest you can get with chart.js and rounded borders is using this answer: https://stackoverflow.com/a/43281198/10387396.

But it doesn't work pretty well with negative values.

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;
  },
});

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

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

//*********//
const d1=  [65, 59, 80, 81,-40, 56, 55, 40 ];
const setColor = data => data < 0 ? '#ff4433': '#3377ee';

var data = {
    labels: ["January", "February", "March", "April", "May", "June", "July", "August"],
    datasets: [
        {
            label: "My First dataset",
            backgroundColor: d1.map(setColor),
            borderColor: d1.map(setColor),
            data: d1
        },
    ],
};

var options = {
    scaleBeginAtZero: false,
    responsive: true,
    barRoundness: 1,
    scales: {
      yAxes: [{
        ticks: {
          suggestedMin: -70,
          suggestedMax: 100
        }
      }]
    }
};

const ctx = "myChart";
const chart = new Chart(ctx, { type: 'roundedBar', data, options });
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="myChart" width="250" height="100"></canvas>

With Echarts it's a bit easier to do that.

const chart = echarts.init(document.getElementById('main'));
const d1 =  [65, 59, 80, 81,-40, 56, 55, 40 ];

// specify chart configuration item and data
const option = {

  tooltip: {},
  legend: {
    data:['Sales']
  },
  xAxis: {
    data: ["January", "February", "March", "April", "May", "June", "July", "August"]
  },
  yAxis: {},
  series: [{
    name: 'Sales',
    type: 'bar',
    barWidth: 20,
    data: d1.map(item => {
      return {
        value: item,
        itemStyle: {
          color: item > 0 ? '#2196F3' : '#F44336',
          barBorderRadius: item > 0 ? [100, 100, 0 ,0] 
                                    : [0 ,0 ,100, 100]
        }
      }
    }),
  }]
};

// use configuration item and data specified to show chart
chart.setOption(option);
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/4.1.0/echarts.min.js"></script>
<div id="main" style="width: 100vw;height:400px;"></div>

If you don't care about rounded borders, but only styling the negative values, you can map your data in order to create an array of colors depending on its value:

const chartData = [65, 59, 80, 81,-40, 56, 55, 40 ];
const data = {
  labels: ["January", "February", "March", "April", "May", "June", "July", "August"],
  datasets: [
    {
        label: "My First dataset",
        backgroundColor: chartData.map(setColor),
        borderColor: chartData.map(setColor),
        data: chartData
    },
  ],
};
// If data < 0 return a red color.
const setColor = data => data < 0 ? '#ff4433': '#3377ee';

Result:

const d1=  [65, 59, 80, 81,-40, 56, 55, 40 ];
const setColor = data => data < 0 ? '#ff4433': '#3377ee';

var data = {
    labels: ["January", "February", "March", "April", "May", "June", "July", "August"],
    datasets: [
        {
            label: "My First dataset",
            backgroundColor: d1.map(setColor),
            borderColor: d1.map(setColor),
            data: d1
        },
    ],
};

var options = {
    scaleBeginAtZero: false,
    responsive: true,
    scales: {
      yAxes: [{
        ticks: {
          suggestedMin: -60,
          suggestedMax: 100
        }
      }]
    }
};

const ctx = "myChart";
const chart = new Chart(ctx, { type: 'bar', data, options });
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script>
<canvas id="myChart" width="250" height="100"></canvas>
André Adriano
  • 419
  • 4
  • 7
0

For the rounded border you can use the new version 3 of the lib, it supports it natively.

For the red bars if they are negative, you can pass an array with all colours for background colour and for every index on which the data is negative change it to red. Should work

Example rounded corners: https://www.chartjs.org/samples/master/charts/bar/border-radius.html

LeeLenalee
  • 27,463
  • 6
  • 45
  • 69