图表j为y轴的不同背景

时间:2017-05-19 08:29:57

标签: javascript css graph chart.js

我在图表js中有一个折线图。我想在y轴上给它一个不同的背景说,0-40是红色,40-70是黄色,70-100是绿色。 y轴的限制始终为100。

   var scatterChart = new Chart(ctx, {
    type: 'line',
    data: {
        datasets: [{
            label: ' Dataset',
            data: [{
                x: 1,
                y: 10
            }, {
                x: 2,
                y: 50
            }, {
                x: 3,
                y: 88
            }, {
                x: 4,
                y: 5
            }]
        }]
    },
    options: {
        scales: {
            xAxes: [{
                type: 'linear',
                position: 'bottom'
            }]
        }
    }
});

如何设置背景。

3 个答案:

答案 0 :(得分:3)

没有内置选项,但是我们可以通过一些代码来实现结果。

var ctx = document.getElementById("chart").getContext("2d");

var scatterChart = new Chart(ctx, {
  type: "line",
  data: {
    datasets: [{
      label: " Dataset",
      data: [{
          x: 1,
          y: 10
        },
        {
          x: 2,
          y: 50
        },
        {
          x: 3,
          y: 88
        },
        {
          x: 4,
          y: 5
        }
      ]
    }]
  },
  options: {
    backgroundRules: [{
        backgroundColor: "red",
        yAxisSegement: 40
      },
      {
        backgroundColor: "yellow",
        yAxisSegement: 70
      },
      {
        backgroundColor: "green",
        yAxisSegement: Infinity
      }
    ],
    scales: {
      xAxes: [{
        type: "linear",
        position: "bottom"
      }],
      yAxes: [{
        color: ["#123456", "#234567"]
      }]
    }
  },
  plugins: [{
    beforeDraw: function(chart) {
      var ctx = chart.chart.ctx;
      var ruleIndex = 0;
      var rules = chart.chart.options.backgroundRules;
      var yaxis = chart.chart.scales["y-axis-0"];
      var xaxis = chart.chart.scales["x-axis-0"];
      var partPercentage = 1 / (yaxis.ticksAsNumbers.length - 1);
      for (var i = yaxis.ticksAsNumbers.length - 1; i > 0; i--) {
        if (yaxis.ticksAsNumbers[i] < rules[ruleIndex].yAxisSegement) {
          ctx.fillStyle = rules[ruleIndex].backgroundColor;
          ctx.fillRect(xaxis.left, yaxis.top + (i - 1) * (yaxis.height * partPercentage), xaxis.width, yaxis.height * partPercentage);
        } else {
          ruleIndex++;
          i++;
        }
      }
    }
  }]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js"></script>
<div class="container">
  <canvas id="chart"></canvas>
</div>

答案 1 :(得分:2)

好吧,您可以尝试类似的操作(难看),在定义渐变的代码段中有一条注释。

当然,可以通过所需的输入属性来确定渐变的颜色和组成。另外,可以使用渐变位置或进行径向渐变而不是线性渐变。

最后一件事,此代码段确实可以改进,基本上我所做的是确定库如何绘制图表以及完成生命周期的哪一部分,然后将其复制到插件中并替换实体画布线性渐变的背景色;)

要改善代码段,我首先尝试使用在Chart对象中定义的方法(例如lineTo()drawArea()),而不是将其复制到插件中,然后实施在options对象中定义的选项以创建线性渐变。

var ctx = document.getElementById("chart").getContext("2d");

var scatterChart = new Chart(ctx, {
  type: "line",
  data: {
    datasets: [{
      label: " Dataset",
      data: [{
          x: 1,
          y: 10
        },
        {
          x: 2,
          y: 50
        },
        {
          x: 3,
          y: 88
        },
        {
          x: 4,
          y: 5
        }
      ]
    }]
  },
  options: {
    scales: {
      xAxes: [{
        type: "linear",
        position: "bottom"
      }]
    }
  },
  plugins: [{
    beforeDatasetDraw: function(chart, options) {
      var metasets = chart._getSortedVisibleDatasetMetas();
      var ctx = chart.ctx;
      var meta, i, el, view, points, mapper, color;

      var clipArea = (ctx, area) => {
        ctx.save();
        ctx.beginPath();
        ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
        ctx.clip();
      };

      var unclipArea = (ctx) => {
        ctx.restore();
      };

      var isDrawable = (point) => {
        return point && !point.skip;
      }

      var lineTo = (ctx, previous, target, flip) => {
        var stepped = target.steppedLine;
        if (stepped) {
          if (stepped === 'middle') {
            var midpoint = (previous.x + target.x) / 2.0;
            ctx.lineTo(midpoint, flip ? target.y : previous.y);
            ctx.lineTo(midpoint, flip ? previous.y : target.y);
          } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) {
            ctx.lineTo(previous.x, target.y);
          } else {
            ctx.lineTo(target.x, previous.y);
          }
          ctx.lineTo(target.x, target.y);
          return;
        }

        if (!target.tension) {
          ctx.lineTo(target.x, target.y);
          return;
        }

        ctx.bezierCurveTo(
          flip ? previous.controlPointPreviousX : previous.controlPointNextX,
          flip ? previous.controlPointPreviousY : previous.controlPointNextY,
          flip ? target.controlPointNextX : target.controlPointPreviousX,
          flip ? target.controlPointNextY : target.controlPointPreviousY,
          target.x,
          target.y);
      }

      var drawArea = (ctx, curve0, curve1, len0, len1) => {
        var i, cx, cy, r;

        if (!len0 || !len1) {
          return;
        }

        // building first area curve (normal)
        ctx.moveTo(curve0[0].x, curve0[0].y);
        for (i = 1; i < len0; ++i) {
          lineTo(ctx, curve0[i - 1], curve0[i]);
        }

        if (curve1[0].angle !== undefined) {
          cx = curve1[0].cx;
          cy = curve1[0].cy;
          r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2));
          for (i = len1 - 1; i > 0; --i) {
            ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true);
          }
          return;
        }

        // joining the two area curves
        ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);

        // building opposite area curve (reverse)
        for (i = len1 - 1; i > 0; --i) {
          lineTo(ctx, curve1[i], curve1[i - 1], true);
        }
      }

      var doFill = (ctx, points, mapper, view, color, loop) => {
        var count = points.length;
        var span = view.spanGaps;
        var curve0 = [];
        var curve1 = [];
        var len0 = 0;
        var len1 = 0;
        var i, ilen, index, p0, p1, d0, d1, loopOffset;

        ctx.beginPath();

        for (i = 0, ilen = count; i < ilen; ++i) {
          index = i % count;
          p0 = points[index]._view;
          p1 = mapper(p0, index, view);
          d0 = isDrawable(p0);
          d1 = isDrawable(p1);

          if (loop && loopOffset === undefined && d0) {
            loopOffset = i + 1;
            ilen = count + loopOffset;
          }

          if (d0 && d1) {
            len0 = curve0.push(p0);
            len1 = curve1.push(p1);
          } else if (len0 && len1) {
            if (!span) {
              drawArea(ctx, curve0, curve1, len0, len1);
              len0 = len1 = 0;
              curve0 = [];
              curve1 = [];
            } else {
              if (d0) {
                curve0.push(p0);
              }
              if (d1) {
                curve1.push(p1);
              }
            }
          }
        }

        drawArea(ctx, curve0, curve1, len0, len1);

        ctx.closePath();
        ctx.fillStyle = color;
        ctx.fill();
      }

      for (i = metasets.length - 1; i >= 0; --i) {
        meta = metasets[i].$filler;

        if (!meta || !meta.visible) {
          continue;
        }

        el = meta.el;
        view = el._view;
        points = el._children || [];
        mapper = meta.mapper;

        // NOTE: HERE IS WHERE THE GRADIENT IS DEFINED. ONE COULD PROBABLY CREATE THE GRADIENT BASED ON INPUT DATA INSIDE THE OPTIONS OBJECT.
        color = ctx.createLinearGradient(chart.width / 2, chart.height, chart.width / 2, 0);
        color.addColorStop(0, 'red');
        color.addColorStop(0.2, 'red');
        color.addColorStop(0.4, 'yellow');
        color.addColorStop(0.6, 'yellow');
        color.addColorStop(0.8, 'green');
        color.addColorStop(1, 'green');

        if (mapper && color && points.length) {
          clipArea(ctx, chart.chartArea);
          doFill(ctx, points, mapper, view, color, el._loop);
          unclipArea(ctx);
        }
      }
    }
  }]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js"></script>
<div class="container">
  <canvas id="chart"></canvas>
</div>

答案 2 :(得分:2)

让我介绍一种通用方法,只要其数据集仅包含零或正值,该方法便适用于任何此类图表。

可以简单地在数据集中定义背景颜色和上限值,如下所示:

bgColors: [
  { color: 'red', upTo: 40 },
  { color: 'yellow', upTo: 70 }, 
  { color: 'green', upTo: 100 }
]

然后您可以extend an existing line chart(即'lineDiffBgColors')并覆盖其update函数。在其中,您将创建一个线性CanvasGradient并添加与上述bgColors的定义相对应的色标。最后,需要将线性渐变分配给backgroundColor的{​​{1}}选项。

dataset

请在下面查看您的增强代码。

this.chart.data.datasets[0].backgroundColor = gradient;
Chart.defaults.lineDiffBgColors = Chart.defaults.line;
Chart.controllers.lineDiffBgColors = Chart.controllers.line.extend({
  update: function(reset) {
    var yAxis = this.chart.scales['y-axis-0'];
    var bgColors = this.chart.data.datasets[0].bgColors.slice().reverse();
    var max = Math.max.apply(null, bgColors.map(o => o.upTo));
    var min = yAxis.getValueForPixel(yAxis.bottom);
    var yTop = yAxis.getPixelForValue(max);
    var gradient = this.chart.chart.ctx.createLinearGradient(0, yTop, 0, yAxis.bottom);
    let offset = 0;
    bgColors.forEach((bgc, i) => {
      gradient.addColorStop(offset, bgc.color);
      if (i + 1 == bgColors.length) {
        offset = 1;
      } else {         
        offset = (max - bgColors[i + 1].upTo) / (max - min);
      }
      gradient.addColorStop(offset, bgc.color);
    });
    this.chart.data.datasets[0].backgroundColor = gradient;
    return Chart.controllers.line.prototype.update.apply(this, arguments);
  }
});

new Chart('myChart', {
  type: 'lineDiffBgColors',
  data: {
    datasets: [{
      label: 'Dataset',
      data: [
        { x: 1, y: 10 }, 
        { x: 2, y: 50 }, 
        { x: 3, y: 88 }, 
        { x: 4, y: 5 }
      ],
      bgColors: [
        { color: 'red', upTo: 40 },
        { color: 'yellow', upTo: 70 }, 
        { color: 'green', upTo: 100 }
      ]
    }]
  },
  options: {
    scales: {
      xAxes: [{
        type: 'linear'
      }]
    }
  }
});

如果您更喜欢平滑的渐变,可以按以下方式更改<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script> <canvas id="myChart" height="100"></canvas>函数内部的bgColors.forEach循环。

update