Спеціальні фонові обмеження для пончика (калібрувальний)


9

Я використовую графік вимірювання на основі ( Chartjs-tsgauge ). Я хочу встановити кольори фону для діаграми окремо від меж калібрування. Проблема з тим, як Charts.JS надає тло, оскільки використовуваний у мене плагін не має коду про фони. Наприклад, у мене є датчик з обмеженнями [0, 20, 40, 60, 80, 100]. Я хочу встановити [0-30]зелене, [30-70]жовте і [70-100]червоне. Поточний код: CodePEN

Ось мої поточні варіанти.

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

І ось мій підхід до встановлення кольорів фону.

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

В даний час Chart.JSвідповідає кольорам 0до iмеж 0в i. Я також подумав намалювати ще одну манекенову діаграму з потрібними кольорами і встановити її на вершині реальної діаграми, але, здається, хитрий спосіб зробити це.


Здрастуйте, я хотів би допомогти вам, але я думаю, що я не повністю дотримуюся того, що ви хочете зробити, ви могли б додати образ того, який очікуваний результат ви отримаєте? на якому тлі ви переглядаєте, за графіком?
SirPeople

Відповіді:


3

Попереднє рішення

Ви можете трохи змінити свій набір даних, щоб досягти цього:

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

Оскільки ви gagueLimitsрозбиті на низку діапазонів 20, тому ви не можете отримати доступ до обмеження 30,

Отже, те, що я тут зробив, є розробленим guageLimitsдля розбитого діапазону 10і тепер 30є доступним 70,

Тепер про кольори, кожен з яких guageLimitsпотребує кольору в backgroundColorмасиві, я їх встановлюю відповідно, перевірте вищевказаний масив. Оскільки жоден із відповідних кольорів не може бути порожнім, можливо, вам доведеться використовувати той самий колір, поки ви не досягнете наступного діапазону кольорів.

Оновлене рішення

Ваші вимоги тепер очищені:

  • Ви хочете, щоб мітки калібру не були надто затишними, тому обмеження 20.
  • І ви хочете розділити кольорові діапазони до 30 та 70 балів.

Тепер, технічно, якщо ви хочете встановити кольоровий діапазон на певну точку, вам потрібно мати це значення в gaugeLimitsмасиві, тож скорочений gaugeLimitsмасив тепер стає таким:

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

Ну, ви не можете встановити кольори відповідно, оскільки у вас є точки, вказані в масиві. Отже, backgroundColorмасив:

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

Тепер заключний трюк

Вам все ще потрібно приховати 30 і 70 від шкали, я намагався переключити showMarkersваш код, схоже, він або дозволяє або відключить видиму шкалу чисел цілком, я намагався надати масив рядків, але він лише приймаєboolean


Chartjs-tsgauge Не надає багато документації про доступні параметри, тому я прокрався до коду і знайшовmarkerFormatFn

Для цього потрібно взяти функцію як параметр, і ви можете надати функцію, яка визначає, що робити з кожним із пунктів маркера.

Отже, я придумав запропонувати функцію лише показувати числа, які можна розділити на 20, без залишку, інакше повернути порожній рядок, ось він:

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

Перевірте фрагмент:

//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>


"Тепер про кольори, кожен ваш guageLimits вимагає кольору в масиві backgroundColor" Це те, чого я НЕ намагаюся робити. В даний час під час малювання guageLimits перевіряє свій індекс і використовує backgroundColor [index] як свій колір фону. Я хочу намалювати тло відповідно до властивості gaugeColors, як видно з питання. А якщо вам цікаво, чому я цього хочу? Тому що я не хочу показувати занадто багато міток на діаграмі, як "10-15-20-25-30-35 ... тощо".
кенарсулейман

@kenarsuleyman це спрацювало? Якщо так, ви можете відзначити відповідь прийнятою :)
Towkir

Так, це прекрасно працює. Велике спасибі за ці зусилля. Я був занадто зайнятий іншою роботою, шкодую про це.
kenarsuleyman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.