window.moonlightServersRealtimeChart = { instances: new Map(), init: function (id, elementId, maxDataPoints, minY, maxY, defaultLabels, defaultDataPoints) { const canvas = document.getElementById(elementId); const labels = []; labels.push(... defaultLabels); const dataPoints = []; dataPoints.push(... defaultDataPoints); const chart = new Chart(canvas, { type: 'line', data: { labels, datasets: [{ data: dataPoints, borderColor: 'oklch(0.58 0.18 270)', backgroundColor: 'rgba(55,138,221,0.15)', borderWidth: 2, pointRadius: 0, tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, animation: { duration: 400, easing: 'easeInOutCubic' }, layout: {padding: 0}, plugins: {legend: {display: false}, tooltip: {enabled: false}}, scales: { x: {display: false}, y: {display: false, min: minY, max: maxY} } } }); this.instances.set(id, { chart: chart, labels: labels, dataPoints: dataPoints, maxDataPoints: maxDataPoints }); }, pushValue: function (id, label, val) { const chartData = this.instances.get(id); const isShifting = chartData.labels.length >= chartData.maxDataPoints; chartData.labels.push(label); chartData.dataPoints.push(val); if (isShifting) { // Animate the new point drawing in first... chartData.chart.update({ duration: 300, easing: 'easeOutCubic', lazy: false }); // ...then silently trim the oldest point after the animation completes setTimeout(() => { chartData.labels.shift(); chartData.dataPoints.shift(); chartData.chart.update('none'); }, 300); } else { chartData.chart.update({ duration: 500, easing: 'easeOutQuart', lazy: false }); } }, destroy: function (id) { const chartData = this.instances.get(id); chartData.chart.destroy(); this.instances.delete(id); } }