Chart.Js。点样式作为线性渐变

问题描述 投票:0回答:1

我试图使这些点具有不同的颜色:默认情况下它应该是背景:线性渐变(243.46deg,#FFC700 -1%,#F52525 131.66%); 如果下一个值小于前一个值,则应显示为背景: Linear-gradient(55.98deg, #E83C3C 11.73%, #9D3C3C 72.51%);

我在画布上找到了一些关于线性渐变的教程,但我的所有观点仍然是黑色的

var trendPriceArray = [
  0,
  109119,
  103610,
  112561,
  0,
  0,
  0,
  101852,
  0,
  99736,
  134382,
  110018
];

var trendPercentArray = [-5,
  8.6, -9.5, -2.1,
  34.7, -18.1
];

var monthLabels = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December'
];

// Filter out data points with trendPriceArray values of 0
var filteredData = [];
for (var i = 0; i < trendPriceArray.length; i++) {
  if (trendPriceArray[i] !== 0) {
    filteredData.push({
      x: monthLabels[i], // X coordinate as month label
      y: trendPriceArray[i], // Y coordinate
      label: trendPercentArray[i] + '%', // Display label above the point
    });
  }
}

// Initialize Chart.js
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: monthLabels, // Use the month labels
    datasets: [{
      label: 'Price Trend',
      data: filteredData, // Use filtered data
      fill: false,
      borderColor: '#4e4e4e', // Set the line graph color to #4e4e4e
      backgroundColor: 'rgba(75, 192, 192, 0.2)',
      borderWidth: 1,
      pointBackgroundColor: [], // Empty array to be filled with point background colors
      pointRadius: 13, // Set the point radius to 13 pixels
      pointHoverRadius: 13, // Set the point hover radius to 13 pixels
    }]
  },
  options: {
    scales: {
      x: {
        type: 'category', // Use category scale with the month labels
        title: {
          display: true,
          text: 'Month',
          font: {
            family: 'Montserrat', // Set the font family to Montserrat
          },
          color: '#4e4e4e', // Set the axis label text color to #4e4e4e
        },
        ticks: {
          font: {
            family: 'Montserrat', // Set the font family to Montserrat
          },
          color: '#4e4e4e', // Set the tick text color to #4e4e4e
        },
      },
      y: {
        display: true,
        title: {
          display: true,
          text: 'Price',
          marginBottom: '10px',
          font: {
            family: 'Montserrat', // Set the font family to Montserrat
          },
          color: '#4e4e4e', // Set the axis label text color to #4e4e4e
        },
        ticks: {
          font: {
            family: 'Montserrat', // Set the font family to Montserrat
          },
          color: '#4e4e4e', // Set the tick text color to #4e4e4e
        },
      }
    },
    plugins: {
      tooltip: {
        enabled: true,
        mode: 'index',
        intersect: false,
      },
      legend: {
        labels: {
          font: {
            family: 'Montserrat', // Set the font family to Montserrat
          },
          color: '#4e4e4e', // Set the legend text color to #4e4e4e
        },
      },
    }
  }
});

function createDefaultGradient(ctx) {
  const gradient = ctx.createLinearGradient(0, 0, 0, 400);
  gradient.addColorStop(0, 'rgba(75, 192, 192, 0.2)');
  gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)');
  return gradient;
}

function createRedGradient(ctx) {
  const gradient = ctx.createLinearGradient(0, 0, 0, 400);
  gradient.addColorStop(0, 'rgba(255, 0, 0, 0.2)');
  gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)');
  return gradient;
}

function createGradientPointStyle(gradient) {
  return function(context) {
    const chart = context.chart;
    const {
      ctx,
      borderWidth
    } = chart;
    const {
      x,
      y
    } = context.p0;
    const pointRadius = 6; // You can adjust this value as needed

    ctx.save();
    ctx.beginPath();
    ctx.arc(x, y, pointRadius, 0, Math.PI * 2);
    ctx.fillStyle = gradient;
    ctx.closePath();
    ctx.fill();
    ctx.restore();
  };
}


// Initialize an empty pointStyle array
myChart.data.datasets[0].pointStyle = [];


for (var i = 0; i < filteredData.length; i++) {
  var gradient = createDefaultGradient(ctx); // Default gradient
  if (i < filteredData.length - 1 && filteredData[i + 1].y < filteredData[i].y) {
    gradient = createRedGradient(ctx); // Red gradient for smaller values
  }
  myChart.data.datasets[0].pointStyle.push(createGradientPointStyle(gradient));
}

myChart.update(); // Update the chart
<!DOCTYPE html>
<html>

<head>
  <!-- Include Chart.js library -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
  <!-- Include Montserrat font from Google Fonts -->
  <link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
</head>

<body>
  <!-- Create a container for the chart -->
  <div>
    <canvas id="myChart" width="400" height="200"></canvas>
  </div>
</body>

</html>

javascript html css charts chart.js
1个回答
0
投票

理论上有两种不同的途径 解决你的问题,但你以错误的方式混合了它们:

  1. 在主画布上画出自己的点;这 只能通过插件来完成,在
    afterDraw
    功能;否则,你画的任何东西都是 被图表的默认绘图覆盖
  2. 使用
    pointStyle
    ,您可以将其分配给 新创建的(小)画布或图像,您可以在其中 绘制(可重复使用的)点符号。

从第二个开始,标准方法就足够了 您的要求,我们可以忘记第一个,这应该只 在其他情况不涵盖的特殊情况下受聘。

此外,您应该考虑时刻/顺序 当代码的每个部分被执行时。在 特别是,如果您使用的是 可编写脚本

pointStyle
,不需要第二阶段 和
chart.update
电话。最重要的是 可编写脚本的选项是为了让您能够使用 刚刚计算的动态值(就像你过滤的
y
值)以避免重绘整个图表。 事实上,过滤发生在点之前 被绘制,因此通过提供给 pointStyle
 的参数
函数,您将找到所需的 
y
 值。

这是代码的更正版本,改动最小:

function createDefaultGradient(ctx, max) { const gradient = ctx.createLinearGradient(0, 0, 0, max); gradient.addColorStop(0, 'rgba(75, 192, 192, 0.2)'); gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)'); return gradient; } function createRedGradient(ctx, max) { const gradient = ctx.createLinearGradient(0, 0, 0, max); gradient.addColorStop(0, 'rgba(255, 0, 0, 0.2)'); gradient.addColorStop(1, 'rgba(255, 255, 255, 0.2)'); return gradient; } function createGradientPointStyle(gradientCreator, pointRadius) { const canvas = document.createElement('canvas'); canvas.width = 2*pointRadius+2; canvas.height = 2*pointRadius+2; const ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.arc(pointRadius+1, pointRadius+1, pointRadius, 0, Math.PI * 2); ctx.fillStyle = gradientCreator(ctx, 2*pointRadius+2); ctx.closePath(); ctx.fill(); return canvas; } function pointStyle(context, opt){ var i = context.dataIndex, data = context.dataset.data; var gradientCreator = createDefaultGradient; if (i < data.length - 1 && data[i + 1].y < data[i].y) { gradientCreator = createRedGradient; // Red gradient for smaller values } var radius = context.active ? opt.hoverRadius : opt.radius; return createGradientPointStyle(gradientCreator, radius) } var trendPriceArray = [ 0, 109119, 103610, 112561, 0, 0, 0, 101852, 0, 99736, 134382, 110018 ]; var trendPercentArray = [-5, 8.6, -9.5, -2.1, 34.7, -18.1 ]; var monthLabels = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; // Filter out data points with trendPriceArray values of 0 var filteredData = []; for (var i = 0; i < trendPriceArray.length; i++) { if (trendPriceArray[i] !== 0) { filteredData.push({ x: monthLabels[i], // X coordinate as month label y: trendPriceArray[i], // Y coordinate label: trendPercentArray[i] + '%', // Display label above the point }); } } // Initialize Chart.js var ctx = document.getElementById('myChart').getContext('2d'); var myChart = new Chart(ctx, { type: 'line', data: { labels: monthLabels, // Use the month labels datasets: [{ label: 'Price Trend', data: filteredData, // Use filtered data // fill: false, borderColor: '#4e4e4e', // Set the line graph color to #4e4e4e //backgroundColor: 'rgba(75, 192, 192, 0.2)', borderWidth: 1, // pointBackgroundColor: [], // Empty array to be filled with point background colors pointRadius: 13, // Set the point radius to 13 pixels pointHoverRadius: 23, // Set the point hover radius to 13 pixels pointStyle }] }, options: { scales: { x: { type: 'category', // Use category scale with the month labels title: { display: true, text: 'Month', font: { family: 'Montserrat', // Set the font family to Montserrat }, color: '#4e4e4e', // Set the axis label text color to #4e4e4e }, ticks: { font: { family: 'Montserrat', // Set the font family to Montserrat }, color: '#4e4e4e', // Set the tick text color to #4e4e4e }, }, y: { display: true, title: { display: true, text: 'Price', marginBottom: '10px', font: { family: 'Montserrat', // Set the font family to Montserrat }, color: '#4e4e4e', // Set the axis label text color to #4e4e4e }, ticks: { font: { family: 'Montserrat', // Set the font family to Montserrat }, color: '#4e4e4e', // Set the tick text color to #4e4e4e }, } }, plugins: { tooltip: { enabled: true, mode: 'index', intersect: false, }, legend: { labels: { font: { family: 'Montserrat', // Set the font family to Montserrat }, color: '#4e4e4e', // Set the legend text color to #4e4e4e }, }, } } });
<div style="min-height: 60vh">
    <canvas id="myChart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>

© www.soinside.com 2019 - 2024. All rights reserved.