chart.js 真实比例的时间分布
chart.js time scatter with true scale
已经有几个类似的帖子,但他们的解决方案要么不完整,要么不适用于现代 chart.js。
问题:如何将此代码更新为具有点间真实距离的时间散点图?目前,它是一个线性图,点与点之间的距离为单位。如您所见,1 月 11 日和 1 月 8 日(3 天)之间的距离与例如 1 月 11 日和 1 月 8 日之间的距离相同。 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
我找到了一个简单的解决方案。
第一步是生成一个 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>
已经有几个类似的帖子,但他们的解决方案要么不完整,要么不适用于现代 chart.js。
问题:如何将此代码更新为具有点间真实距离的时间散点图?目前,它是一个线性图,点与点之间的距离为单位。如您所见,1 月 11 日和 1 月 8 日(3 天)之间的距离与例如 1 月 11 日和 1 月 8 日之间的距离相同。 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
我找到了一个简单的解决方案。
第一步是生成一个 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>