Giới hạn nền tùy chỉnh cho Donut (Máy đo)


9

Tôi đang sử dụng biểu đồ đo dựa trên ( Chartjs-tsgauge ). Tôi muốn đặt màu nền cho biểu đồ tách biệt với giới hạn đo. Vấn đề với cách Charts.JS biểu hiện nền vì plugin tôi đã sử dụng không có mã về nền. Ví dụ, tôi có một Máy đo có giới hạn [0, 20, 40, 60, 80, 100]. Tôi muốn đặt [0-30]thành màu xanh lá cây, [30-70]màu vàng và [70-100]màu đỏ. Mã hiện tại: CodePEN

Đây là lựa chọn hiện tại của tôi.

var ctx = document.getElementById("canvas").getContext("2d");
new Chart(ctx, {
    type: "tsgauge",
    data: {
        datasets: [{
            backgroundColor: ["#0fdc63", "#fd9704", "#ff7143"],
            borderWidth: 0,
            gaugeData: {
                value: 7777,
                valueColor: "#ff7143"
            },
            gaugeLimits: [0, 3000, 7000, 10000]
        }]
    },
    options: {
            events: [],
            showMarkers: true
    }
});

Và đây là cách tiếp cận của tôi để thiết lập màu nền.

new Chart(ctx, {
    type: "tsgauge",
    data: {
        datasets: [{
            backgroundColor: ["#0fdc63", "#fd9704", "#ff7143"],
            borderWidth: 0,
            gaugeData: {
                value: 50,
                valueColor: "#ff7143"
            },
            gaugeLimits: [0, 20, 40, 60, 80, 100],
            gaugeColors: [{
                min: 0,
                max: 30,
                color: ""
                }, {
                min: 30,
                max: 70,
                color: ""
             },{
                min:70,
                max:100,
                color: ""
             }]
         }]
    },
    options: {
        events: [],
        showMarkers: true
    }
});

Hiện nay Chart.JSphù hợp với màu sắc 0để icó giới hạn 0để i. Tôi cũng nghĩ rằng vẽ một biểu đồ giả khác với màu sắc mong muốn và đặt nó lên trên biểu đồ thực nhưng có vẻ cách khéo léo để làm điều này.


Xin chào, tôi muốn giúp bạn nhưng tôi nghĩ tôi không hoàn toàn làm theo những gì bạn muốn làm, bạn có thể thêm một hình ảnh về sản lượng dự kiến ​​mà bạn sẽ nhận được không? nền tảng nào bạn đang đề cập đến, đằng sau biểu đồ?
SirPeople

Câu trả lời:


3

Giải pháp trước

Bạn có thể thay đổi tập dữ liệu của mình một chút để đạt được điều đó:

backgroundColor: ["green", "green", "green", "yellow", "yellow", "yellow", "yellow", "red", "red", "red"],
gaugeLimits: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],

Vì hiện tại bạn gagueLimitsđang chia thành một phạm vi 20, vì vậy bạn không thể truy cập giới hạn 30,

Vì vậy, những gì tôi đã làm ở đây được xây dựng guageLimitsthành một phạm vi phân chia 10và bây giờ 30có thể truy cập được, vì vậy 70,

Bây giờ về màu sắc, mỗi màu của bạn guageLimitsyêu cầu một màu trong backgroundColormảng, tôi đặt chúng cho phù hợp, kiểm tra mảng trên. Vì không có màu nào tương ứng có thể trống, bạn có thể cần sử dụng cùng màu cho đến khi bạn đạt được dải màu tiếp theo.

Giải pháp cập nhật

Yêu cầu của bạn đã được xóa ngay bây giờ:

  • Bạn muốn các nhãn đo không quá ấm cúng, đó là lý do tại sao giới hạn 20.
  • Và bạn muốn tách các phạm vi màu đến 30 và 70 điểm.

Bây giờ, về mặt kỹ thuật nếu bạn muốn đặt phạm vi màu thành một điểm cụ thể, bạn cần phải có giá trị đó trên gaugeLimitsmảng, vì vậy gaugeLimitsmảng giảm của bạn bây giờ trở thành như sau:

gaugeLimits: [0, 20, 30, 40, 60, 70, 80, 100],

Chà, không, bạn có thể đặt màu cho phù hợp vì bạn có các điểm được chỉ định trong mảng. Do đó, backgroundColormảng là:

backgroundColor: ["#0fdc63", "#0fdc63", "#fd9704", "#fd9704", "#fd9704", "#ff7143", "#ff7143"]

Bây giờ, Trick cuối cùng

Bạn vẫn cần ẩn 30 và 70 khỏi thang đo, tôi đã cố gắng chuyển showMarkersmã trong mã của bạn, có vẻ như nó cho phép hoặc vô hiệu hóa hoàn toàn thang số có thể nhìn thấy, tôi đã cố gắng cung cấp một chuỗi các chuỗi, nhưng nó chỉ chấp nhậnboolean


Chartjs-tsgauge Không cung cấp nhiều tài liệu về các tùy chọn có sẵn, vì vậy tôi đã tìm hiểu mã và tìm thấymarkerFormatFn

Những gì nó làm là lấy một hàm làm tham số và bạn có thể cung cấp một hàm cho biết phải làm gì với từng mục đánh dấu của bạn.

Vì vậy, tôi đã nảy ra một ý tưởng là cung cấp một hàm để chỉ hiển thị các số có thể chia cho 20 mà không có bất kỳ số dư nào, nếu không thì trả về chuỗi rỗng, đây là:

markerFormatFn: n => n % 20 === 0 ? n.toString() : '',

Kiểm tra đoạn trích:

//Gauge Plugin
(function() {
  if (!window.Chart) {
    return;
  }

  function GaugeChartHelper() {}
  GaugeChartHelper.prototype.setup = function(chart, config) {
    this.chart = chart;
    this.ctx = chart.ctx;
    this.limits = config.data.datasets[0].gaugeLimits;
    this.data = config.data.datasets[0].gaugeData;
    var options = chart.options;
    this.fontSize = options.defaultFontSize;
    this.fontStyle = options.defaultFontFamily;
    this.fontColor = options.defaultFontColor;
    this.ctx.textBaseline = "alphabetic";
    this.arrowAngle = 25 * Math.PI / 180;
    this.arrowColor = config.options.indicatorColor || options.arrowColor;
    this.showMarkers =
      typeof config.options.showMarkers === "undefined" ?
      true :
      config.options.showMarkers;
    if (config.options.markerFormatFn) {
      this.markerFormatFn = config.options.markerFormatFn;
    } else {
      this.markerFormatFn = function(value) {
        return value;
      };
    }
  };
  GaugeChartHelper.prototype.applyGaugeConfig = function(chartConfig) {
    this.calcLimits();
    chartConfig.data.datasets[0].data = this.doughnutData;
    var ctx = this.ctx;
    var labelsWidth = this.limits.map(
      function(label) {
        var text = this.markerFormatFn(label);
        return ctx.measureText(text).width;
      }.bind(this)
    );
    var padding = Math.max.apply(this, labelsWidth) + this.chart.width / 35;
    var heightRatio = this.chart.height / 50;
    chartConfig.options.layout.padding = {
      top: this.fontSize + heightRatio,
      left: padding,
      right: padding,
      bottom: heightRatio * 2
    };
  };
  GaugeChartHelper.prototype.calcLimits = function() {
    var limits = this.limits;
    var data = [];
    var total = 0;
    for (var i = 1, ln = limits.length; i < ln; i++) {
      var dataValue = Math.abs(limits[i] - limits[i - 1]);
      total += dataValue;
      data.push(dataValue);
    }
    this.doughnutData = data;
    var minValue = limits[0];
    var maxValue = limits[limits.length - 1];
    this.isRevers = minValue > maxValue;
    this.minValue = this.isRevers ? maxValue : minValue;
    this.totalValue = total;
  };
  GaugeChartHelper.prototype.updateGaugeDimensions = function() {
    var chartArea = this.chart.chartArea;
    this.gaugeRadius = this.chart.innerRadius;
    this.gaugeCenterX = (chartArea.left + chartArea.right) / 2;
    this.gaugeCenterY =
      (chartArea.top + chartArea.bottom + this.chart.outerRadius) / 2;
    this.arrowLength = this.chart.radiusLength * 2;
  };
  GaugeChartHelper.prototype.getCoordOnCircle = function(r, alpha) {
    return {
      x: r * Math.cos(alpha),
      y: r * Math.sin(alpha)
    };
  };
  GaugeChartHelper.prototype.getAngleOfValue = function(value) {
    var result = 0;
    var gaugeValue = value - this.minValue;
    if (gaugeValue <= 0) {
      result = 0;
    } else if (gaugeValue >= this.totalValue) {
      result = Math.PI;
    } else {
      result = Math.PI * gaugeValue / this.totalValue;
    }
    if (this.isRevers) {
      return Math.PI - result;
    } else {
      return result;
    }
  };
  GaugeChartHelper.prototype.renderLimitLabel = function(value) {
    var ctx = this.ctx;
    var angle = this.getAngleOfValue(value);
    var coord = this.getCoordOnCircle(
      this.chart.outerRadius + this.chart.radiusLength / 2,
      angle
    );
    var align;
    var diff = angle - Math.PI / 2;
    if (diff > 0) {
      align = "left";
    } else if (diff < 0) {
      align = "right";
    } else {
      align = "center";
    }
    ctx.textAlign = align;
    ctx.font = this.fontSize + "px " + this.fontStyle;
    ctx.fillStyle = this.fontColor;
    var text = this.markerFormatFn(value);
    ctx.fillText(
      text,
      this.gaugeCenterX - coord.x,
      this.gaugeCenterY - coord.y
    );
  };
  GaugeChartHelper.prototype.renderLimits = function() {
    for (var i = 0, ln = this.limits.length; i < ln; i++) {
      this.renderLimitLabel(this.limits[i]);
    }
  };
  GaugeChartHelper.prototype.renderValueLabel = function() {
    var label = this.data.value.toString();
    var ctx = this.ctx;
    ctx.font = "30px " + this.fontStyle;
    var stringWidth = ctx.measureText(label).width;
    var elementWidth = 0.75 * this.gaugeRadius * 2;
    var widthRatio = elementWidth / stringWidth;
    var newFontSize = Math.floor(30 * widthRatio);
    var fontSizeToUse = Math.min(newFontSize, this.gaugeRadius);
    ctx.textAlign = "center";
    ctx.font = fontSizeToUse + "px " + this.fontStyle;
    ctx.fillStyle = this.data.valueColor || this.fontColor;
    ctx.fillText(label, this.gaugeCenterX, this.gaugeCenterY);
  };
  GaugeChartHelper.prototype.renderValueArrow = function(value) {
    var angle = this.getAngleOfValue(
      typeof value === "number" ? value : this.data.value
    );
    this.ctx.globalCompositeOperation = "source-over";
    this.renderArrow(
      this.gaugeRadius,
      angle,
      this.arrowLength,
      this.arrowAngle,
      this.arrowColor
    );
  };
  GaugeChartHelper.prototype.renderSmallValueArrow = function(value) {
    var angle = this.getAngleOfValue(value);
    this.ctx.globalCompositeOperation = "source-over";
    this.renderArrow(
      this.gaugeRadius - 1,
      angle,
      this.arrowLength - 1,
      this.arrowAngle,
      this.arrowColor
    );
  };
  GaugeChartHelper.prototype.clearValueArrow = function(value) {
    var angle = this.getAngleOfValue(value);
    this.ctx.lineWidth = 2;
    this.ctx.globalCompositeOperation = "destination-out";
    this.renderArrow(
      this.gaugeRadius - 1,
      angle,
      this.arrowLength + 1,
      this.arrowAngle,
      "#FFFFFF"
    );
    this.ctx.stroke();
  };
  GaugeChartHelper.prototype.renderArrow = function(
    radius,
    angle,
    arrowLength,
    arrowAngle,
    arrowColor
  ) {
    var coord = this.getCoordOnCircle(radius, angle);
    var arrowPoint = {
      x: this.gaugeCenterX - coord.x,
      y: this.gaugeCenterY - coord.y
    };
    var ctx = this.ctx;
    ctx.fillStyle = arrowColor;
    ctx.beginPath();
    ctx.moveTo(arrowPoint.x, arrowPoint.y);
    coord = this.getCoordOnCircle(arrowLength, angle + arrowAngle);
    ctx.lineTo(arrowPoint.x + coord.x, arrowPoint.y + coord.y);
    coord = this.getCoordOnCircle(arrowLength, angle - arrowAngle);
    ctx.lineTo(arrowPoint.x + coord.x, arrowPoint.y + coord.y);
    ctx.closePath();
    ctx.fill();
  };
  GaugeChartHelper.prototype.animateArrow = function() {
    var stepCount = 30;
    var animateTimeout = 300;
    var gaugeValue = this.data.value - this.minValue;
    var step = gaugeValue / stepCount;
    var i = 0;
    var currentValue = this.minValue;
    var interval = setInterval(
      function() {
        i++;
        this.clearValueArrow(currentValue);
        if (i > stepCount) {
          clearInterval(interval);
          this.renderValueArrow();
        } else {
          currentValue += step;
          this.renderSmallValueArrow(currentValue);
        }
      }.bind(this),
      animateTimeout / stepCount
    );
  };
  Chart.defaults.tsgauge = {
    animation: {
      animateRotate: true,
      animateScale: false
    },
    cutoutPercentage: 95,
    rotation: Math.PI,
    circumference: Math.PI,
    legend: {
      display: false
    },
    scales: {},
    arrowColor: "#444"
  };
  Chart.controllers.tsgauge = Chart.controllers.doughnut.extend({
    initialize: function(chart) {
      var gaugeHelper = (this.gaugeHelper = new GaugeChartHelper());
      gaugeHelper.setup(chart, chart.config);
      gaugeHelper.applyGaugeConfig(chart.config);
      chart.config.options.animation.onComplete = function(chartElement) {
        gaugeHelper.updateGaugeDimensions();
        gaugeHelper.animateArrow();
      };
      Chart.controllers.doughnut.prototype.initialize.apply(this, arguments);
    },
    draw: function() {
      Chart.controllers.doughnut.prototype.draw.apply(this, arguments);
      var gaugeHelper = this.gaugeHelper;
      gaugeHelper.updateGaugeDimensions();
      gaugeHelper.renderValueLabel();
      if (gaugeHelper.showMarkers) {
        gaugeHelper.renderLimits();
      }
      gaugeHelper.renderSmallValueArrow(gaugeHelper.minValue);
    }
  });
})();

//Chart setup

var ctx = document.getElementById("chart3").getContext("2d");
new Chart(ctx, {
  type: "tsgauge",
  data: {
    datasets: [{
      backgroundColor: ["#0fdc63", "#0fdc63", "#fd9704", "#fd9704", "#fd9704", "#ff7143", "#ff7143"],
      borderWidth: 0,
      gaugeData: {
        value: 50,
        valueColor: "#ff7143"
      },
      gaugeLimits: [0, 20, 30, 40, 60, 70, 80, 100],
    }]
  },
  options: {
    events: [],
    showMarkers: true,
    markerFormatFn: n => n % 20 === 0 ? n.toString() : '',
  }
});
.gauge {
  width: 500px;
  height: 400px;
}
<link href="https://cdn.jsdelivr.net/npm/chart.js@2.9.2/dist/Chart.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.2/dist/Chart.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>


<div class="gauge">
  <canvas id="chart3"></canvas>
</div>


"Bây giờ về màu sắc, mỗi guageLimits của bạn yêu cầu một màu trong mảng nềnColor" Đó là điều tôi KHÔNG cố gắng làm. Hiện tại, khi vẽ guageLimits kiểm tra chỉ mục của nó và sử dụng backgroundColor [index] làm màu nền của nó. Tôi muốn vẽ nền theo thuộc tính của gauColors như đã thấy trong câu hỏi. Và nếu bạn tò mò về lý do tại sao tôi muốn điều này? Bởi vì tôi không muốn hiển thị quá nhiều nhãn trên biểu đồ như "10-15-20-25-30-35 ... vv".
kenarsuleyman

@kenarsuleyman đã làm việc đó? Nếu vậy, bạn có thể đánh dấu câu trả lời là được chấp nhận :)
Towkir

Vâng, nó hoạt động hoàn hảo. Cảm ơn rất nhiều cho nỗ lực đó. Tôi đã quá bận rộn với một công việc khác xin lỗi về điều đó.
kenarsuleyman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.