【问题标题】:chart.js time scatter with true scale具有真实比例的 chart.js 时间散点图
【发布时间】:2021-09-08 08:07:55
【问题描述】:

已经有几篇类似的帖子,但他们的解决方案要么不完整,要么不适用于现代 chart.js

问题:如何将此代码更新为具有点之间真实距离的时间散点图?目前,它是一个具有点之间单位距离的线性图。如您所见,1 月 11 日和 1 月 8 日(3 天)之间的距离与 e.g. 1 月 8 日和 1 月 7 日(1 天)。

let testPoints =  [
    { x: '2017-01-06 00:00:00', y: '20' },
    { x: '2017-01-07 00:00:00', y: '17' },
    { x: '2017-01-08 00:00:00', y: '14' },
    { x: '2017-01-11 00:00:00', y: '7' },
  ]

var s1 = {
  label:'Overall activity',
  borderColor: '#33b5e5',
  data: testPoints
};

config = {
    type: 'line',
    data: { datasets: [s1] },
    options: {
        plugins: {
            title: {
                display: true,
                text: 'Overall activity plot',
                font: {
                    size: 20
                }
            }
        },
        legend: {
            display: false
        },
        scales: {
            xAxes: [{
                type: 'time',
                weight : 0,
                time: {
                    unit:'day'
                }
            }],
        }
    }
};

new Chart(ctx, config);

注意:如果我包含 moment.js v2.29.1 并使用 x: moment('2017-01-06 00:00:00'),则绘图根本不会绘图,并出现以下错误:

Uncaught SyntaxError: Unexpected token export moment.js:5662
Uncaught ReferenceError: moment is not defined

注意:如果我只设置config.type: 'scatter',情节就会变空。

堆栈:

chart.js v3.5.1

【问题讨论】:

    标签: javascript chart.js momentjs


    【解决方案1】:

    我找到了一个简单的解决方案。 第一步是生成一个 labels 数组,所有日期都包含在 x 的最小值和最大值范围内。

    const labels = [];
    const help = moment(testPoints[0].x);
    
    while (moment(help).isSameOrBefore(moment(testPoints[testPoints.length - 1].x))) {
      labels.push(help.format('YYYY-MM-DD HH:mm:ss'));
      help.add(1, 'day');
    }
    

    之后,您可以像这样在config.data 中添加labels

    const config = {
      type: 'line',
      data: { 
        labels, 
        datasets: [s1]
      },
    
      options: {
        plugins: {
          ...
        }
      },
      ...
    }
    

    这里有一个工作示例

    function generateDateLabelsFromDataset (dataset) {
      const labels = [];
      let max, min = undefined;
      for (const { x } of testPoints) { // I don't know if your dataset is ordered so i get the min and max for generate labels, if your data set is ordered min and max are the first and the last x value of your array
        if (!max || moment(x).isAfter(max)) max = moment(x);
        if (!min || moment(x).isBefore(min)) min = moment(x);
      }
    
      while (moment(min).isSameOrBefore(max)) {
        labels.push(min.format('YYYY-MM-DD HH:mm:ss'));
        min.add(1, 'day');
      }
      return labels;
    }
    
    const testPoints = [
      { x: '2017-01-06 00:00:00', y: '20' },
      { x: '2017-01-07 00:00:00', y: '17' },
      { x: '2017-01-08 00:00:00', y: '14' },
      { x: '2017-01-11 00:00:00', y: '7' },
    ];
    
    const s1 = {
      label: 'Overall activity',
      borderColor: '#33b5e5',
      data: testPoints
    };
    
    const labels = generateDateLabelsFromDataset(testPoints)
    
    const config = {
      type: 'line',
      data: {
        labels,
        datasets: [s1]
      },
    
      options: {
        plugins: {
          title: {
            display: true,
            text: 'Overall activity plot',
            font: {
              size: 20
            }
          }
        },
        legend: {
          display: false
        },
        scales: {
          xAxes: [{
            type: 'time',
            weight: 0,
            time: {
              unit: 'day'
            }
          }],
        }
      }
    };
    
    var ctx = document.getElementById('myChart').getContext('2d');
    var myChart = new Chart(ctx, config);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
    
    <canvas id="myChart" width="200" height="200"></canvas>

    新尝试

    function generateDateLabelsFromDataset (dataset) {
      const labels = [];
      let max, min = undefined;
      for (const { x } of testPoints) { // I don't know if your dataset is ordered so i get the min and max for generate labels, if your data set is ordered min and max are the first and the last x value of your array
        if (!max || moment(x).isAfter(max)) max = moment(x);
        if (!min || moment(x).isBefore(min)) min = moment(x);
      }
    
      min.startOf('day'); // you can remove this 2 lines if you don't want the blank space before and after the segment
      max.endOf('day');
    
      while (moment(min).isSameOrBefore(max)) {
        labels.push(min.format('YYYY-MM-DD HH:mm:ss'));
        min.add(1, 'hour'); // here put your littler unit that you want (hour, minute, second ecc..)
      }
      return labels;
    }
    
    const testPoints = [
      { x: '2017-01-06 12:00:00', y: '20' },
      { x: '2017-01-07 00:00:00', y: '17' },
      { x: '2017-01-08 00:00:00', y: '14' },
      { x: '2017-01-11 00:00:00', y: '7' },
    ];
    
    const s1 = {
      label: 'Overall activity',
      borderColor: '#33b5e5',
      data: testPoints
    };
    
    const labels = generateDateLabelsFromDataset(testPoints)
    
    const config = {
      type: 'line',
      data: {
        labels,
        datasets: [s1]
      },
    
      options: {
        plugins: {
          title: {
            display: true,
            text: 'Overall activity plot',
            font: {
              size: 20
            }
          }
        },
        legend: {
          display: false
        },
        scales: {
          xAxes: [{
            type: 'time',
            weight: 0,
            time: {
              unit: 'day'
            },
            ticks: {
              callback: function(val, index) {
                // Hide the label of every 2nd dataset
                return index % 24 === 0 ? this.getLabelForValue(val) : ''; // is the hours, but if you want use minutes, you must change 24 in 24 * 60 if you want keep just the day like label
              }
            }
            
          }],
        }
      }
    };
    
    var ctx = document.getElementById('myChart').getContext('2d');
    var myChart = new Chart(ctx, config);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.1/chart.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
    
    <canvas id="myChart" width="200" height="200"></canvas>

    【讨论】:

    • 感谢您的回答。您提出的解决方案对于简单的情况已经足够了,但不能一概而论。因此我无法接受。如果将第一个点从“'2017-01-06 00:00:00”更改为“'2017-01-06 12:00:00'”,则情节看起来很糟糕。
    • 我查看了文档,我认为没有官方的方法可以得到你想要的东西(chartjs.org/docs/next/axes/cartesian/timeseries.html 前 2 行)。您可以调整我的代码以获得更小的段并修复 x 标签(我用新代码重新编辑我的答案)。
    猜你喜欢
    • 2017-12-17
    • 2021-12-17
    • 1970-01-01
    • 2021-06-06
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 2013-06-22
    • 2017-08-08
    相关资源
    最近更新 更多