mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-08 21:29:07 +02:00
216 lines
5.8 KiB
HTML
216 lines
5.8 KiB
HTML
<!doctype html>
|
|
<html>
|
|
|
|
<head>
|
|
<title>Line Chart</title>
|
|
<script src="../../dist/chart.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/luxon@1.24.1"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@0.2.1"></script>
|
|
<script src="../utils.js"></script>
|
|
<style>
|
|
canvas {
|
|
-moz-user-select: none;
|
|
-webkit-user-select: none;
|
|
-ms-user-select: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div style="width:1000px">
|
|
<p>This example demonstrates a time series scale with custom logic for generating minor and major ticks. Major ticks are bolded</p>
|
|
<p>For more specific functionality for financial charts, please see <a href="https://github.com/chartjs/chartjs-chart-financial">chartjs-chart-financial</a></p>
|
|
<canvas id="chart1"></canvas>
|
|
</div>
|
|
<br>
|
|
<br>
|
|
Chart Type:
|
|
<select id="type">
|
|
<option value="line">Line</option>
|
|
<option value="bar">Bar</option>
|
|
</select>
|
|
<select id="unit">
|
|
<option value="second">Second</option>
|
|
<option value="minute">Minute</option>
|
|
<option value="hour">Hour</option>
|
|
<option value="day" selected>Day</option>
|
|
<option value="month">Month</option>
|
|
<option value="year">Year</option>
|
|
</select>
|
|
<button id="update">update</button>
|
|
<script>
|
|
// Generate data between the stock market hours of 9:30am - 4pm.
|
|
// This method is slow and unoptimized, but in real life we'd be fetching it from the server.
|
|
function generateData() {
|
|
const unit = document.getElementById('unit').value;
|
|
|
|
function unitLessThanDay() {
|
|
return unit === 'second' || unit === 'minute' || unit === 'hour';
|
|
}
|
|
|
|
function beforeNineThirty(date) {
|
|
return date.hour < 9 || (date.hour === 9 && date.minute < 30);
|
|
}
|
|
|
|
|
|
function after4pm(date) {
|
|
return date.hour >= 16;
|
|
}
|
|
|
|
// Returns true if outside 9:30am-4pm on a weekday
|
|
function outsideMarketHours(date) {
|
|
if (date.weekday > 5) {
|
|
return true;
|
|
}
|
|
if (unitLessThanDay() && (beforeNineThirty(date) || after4pm(date))) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function randomNumber(min, max) {
|
|
return Math.random() * (max - min) + min;
|
|
}
|
|
|
|
function randomBar(date, lastClose) {
|
|
const open = randomNumber(lastClose * 0.95, lastClose * 1.05).toFixed(2);
|
|
const close = randomNumber(open * 0.95, open * 1.05).toFixed(2);
|
|
return {
|
|
x: date.valueOf(),
|
|
y: close
|
|
};
|
|
}
|
|
|
|
let date = luxon.DateTime.fromFormat('Jan 01 1990', 'LLL dd yyyy');
|
|
const now = luxon.DateTime.local();
|
|
const data = [];
|
|
const lessThanDay = unitLessThanDay();
|
|
const plus = {};
|
|
plus[unit] = 1;
|
|
for (; data.length < 600 && date < now; date = date.plus(plus).startOf(unit)) {
|
|
if (outsideMarketHours(date)) {
|
|
if (!lessThanDay || !beforeNineThirty(date)) {
|
|
date = date.plus({day: date.weekday >= 5 ? 8 - date.weekday : 1});
|
|
}
|
|
if (lessThanDay) {
|
|
date = date.set({hour: 9, minute: 30});
|
|
}
|
|
}
|
|
data.push(randomBar(date, data.length > 0 ? data[data.length - 1].y : 30));
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
const ctx = document.getElementById('chart1').getContext('2d');
|
|
ctx.canvas.width = 1000;
|
|
ctx.canvas.height = 300;
|
|
|
|
const color = Chart.helpers.color;
|
|
const cfg = {
|
|
data: {
|
|
datasets: [{
|
|
label: 'CHRT - Chart.js Corporation',
|
|
backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
|
|
borderColor: window.chartColors.red,
|
|
data: generateData(),
|
|
type: 'line',
|
|
pointRadius: 0,
|
|
fill: false,
|
|
tension: 0,
|
|
borderWidth: 2
|
|
}]
|
|
},
|
|
options: {
|
|
animation: {
|
|
duration: 0
|
|
},
|
|
scales: {
|
|
x: {
|
|
type: 'timeseries',
|
|
offset: true,
|
|
ticks: {
|
|
major: {
|
|
enabled: true,
|
|
},
|
|
font: function(context) {
|
|
return context.tick && context.tick.major ? {style: 'bold'} : undefined;
|
|
},
|
|
source: 'data',
|
|
autoSkip: true,
|
|
autoSkipPadding: 75,
|
|
maxRotation: 0,
|
|
sampleSize: 100
|
|
},
|
|
// Custom logic that chooses major ticks by first timestamp in time period
|
|
// E.g. if March 1 & 2 are missing from dataset because they're weekends, we pick March 3 to be beginning of month
|
|
afterBuildTicks: function(scale) {
|
|
const majorUnit = scale._majorUnit;
|
|
const ticks = scale.ticks;
|
|
const firstTick = ticks[0];
|
|
|
|
let val = luxon.DateTime.fromMillis(ticks[0].value);
|
|
if ((majorUnit === 'minute' && val.second === 0)
|
|
|| (majorUnit === 'hour' && val.minute === 0)
|
|
|| (majorUnit === 'day' && val.hour === 9)
|
|
|| (majorUnit === 'month' && val.day <= 3 && val.weekday === 1)
|
|
|| (majorUnit === 'year' && val.month === 1)) {
|
|
firstTick.major = true;
|
|
} else {
|
|
firstTick.major = false;
|
|
}
|
|
let lastMajor = val.get(majorUnit);
|
|
|
|
for (let i = 1; i < ticks.length; i++) {
|
|
const tick = ticks[i];
|
|
val = luxon.DateTime.fromMillis(tick.value);
|
|
const currMajor = val.get(majorUnit);
|
|
tick.major = currMajor !== lastMajor;
|
|
lastMajor = currMajor;
|
|
}
|
|
scale.ticks = ticks;
|
|
}
|
|
},
|
|
y: {
|
|
type: 'linear',
|
|
gridLines: {
|
|
drawBorder: false
|
|
},
|
|
scaleLabel: {
|
|
display: true,
|
|
labelString: 'Closing price ($)'
|
|
}
|
|
}
|
|
},
|
|
tooltips: {
|
|
intersect: false,
|
|
mode: 'index',
|
|
callbacks: {
|
|
label: function(context) {
|
|
let label = context.dataset.label || '';
|
|
if (label) {
|
|
label += ': ';
|
|
}
|
|
label += context.dataPoint.y.toFixed(2);
|
|
return label;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const chart = new Chart(ctx, cfg);
|
|
|
|
document.getElementById('update').addEventListener('click', function() {
|
|
const type = document.getElementById('type').value;
|
|
const dataset = chart.config.data.datasets[0];
|
|
dataset.type = type;
|
|
dataset.data = generateData();
|
|
chart.update();
|
|
});
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|