class ChartViz extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
this.initChart();
}
render() {
this.shadowRoot.innerHTML = `
`;
}
initChart() {
const canvas = this.shadowRoot.getElementById('chartCanvas');
const ctx = canvas.getContext('2d');
const type = this.getAttribute('type') || 'line';
let width, height;
const resize = () => {
width = canvas.width = canvas.offsetWidth;
height = canvas.height = canvas.offsetHeight;
};
new ResizeObserver(resize).observe(this);
// Data generation
const generateData = (points) => {
return Array.from({ length: points }, (_, i) => ({
x: i,
y: Math.random() * 0.5 + 0.25 + Math.sin(i * 0.1) * 0.15
}));
};
let data = generateData(type === 'training' ? 100 : 24);
let offset = 0;
const drawLineChart = () => {
ctx.clearRect(0, 0, width, height);
// Grid
ctx.strokeStyle = '#1e293b';
ctx.lineWidth = 1;
for (let i = 0; i < 5; i++) {
const y = (height / 4) * i;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
// Line
ctx.beginPath();
ctx.strokeStyle = '#10b981';
ctx.lineWidth = 2;
data.forEach((point, i) => {
const x = (i / (data.length - 1)) * width;
const y = height - (point.y * height * 0.8 + height * 0.1);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
// Gradient fill
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, 'rgba(16, 185, 129, 0.3)');
gradient.addColorStop(1, 'rgba(16, 185, 129, 0)');
ctx.lineTo(width, height);
ctx.lineTo(0, height);
ctx.fillStyle = gradient;
ctx.fill();
// Points
data.forEach((point, i) => {
if (i % 4 === 0) {
const x = (i / (data.length - 1)) * width;
const y = height - (point.y * height * 0.8 + height * 0.1);
ctx.beginPath();
ctx.arc(x, y, 4, 0, Math.PI * 2);
ctx.fillStyle = '#10b981';
ctx.fill();
}
});
};
const drawBarChart = () => {
ctx.clearRect(0, 0, width, height);
const barWidth = width / data.length - 4;
data.forEach((point, i) => {
const x = i * (width / data.length) + 2;
const barHeight = point.y * height * 0.8;
const y = height - barHeight;
// Color based on value
const hue = 120 + (1 - point.y) * 60; // Green to yellow
ctx.fillStyle = `hsl(${hue}, 70%, 50%)`;
// Rounded top bars
ctx.beginPath();
ctx.roundRect(x, y, barWidth, barHeight, [4, 4, 0, 0]);
ctx.fill();
});
};
const drawTrainingChart = () => {
ctx.clearRect(0, 0, width, height);
// Loss line (decreasing)
ctx.beginPath();
ctx.strokeStyle = '#f97316';
ctx.lineWidth = 2;
data.forEach((point, i) => {
const x = (i / (data.length - 1)) * width;
const normalizedLoss = 1 - (i / data.length) * 0.8 + Math.random() * 0.05;
const y = height - (normalizedLoss * height * 0.7 + height * 0.15);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
// Accuracy line (increasing)
ctx.beginPath();
ctx.strokeStyle = '#3b82f6';
ctx.lineWidth = 2;
data.forEach((point, i) => {
const x = (i / (data.length - 1)) * width;
const normalizedAcc = 0.5 + (i / data.length) * 0.48 + Math.random() * 0.02;
const y = height - (normalizedAcc * height * 0.7 + height * 0.15);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
// Legend
ctx.fillStyle = '#f97316';
ctx.fillRect(10, 10, 12, 12);
ctx.fillStyle = '#94a3b8';
ctx.font = '11px sans-serif';
ctx.fillText('Loss', 28, 20);
ctx.fillStyle = '#3b82f6';
ctx.fillRect(80, 10, 12, 12);
ctx.fillStyle = '#94a3b8';
ctx.fillText('Accuracy', 98, 20);
};
const animate = () => {
if (type === 'training') {
// Update data occasionally
offset++;
if (offset % 60 === 0) {
data = data.map((point, i) => ({
...point,
y: Math.random() * 0.5 + 0.25 + Math.sin(i * 0.1 + offset * 0.01) * 0.15
}));
}
} else if (type === 'line') {
// Shift data for real-time effect
if (offset % 10 === 0) {
data.shift();
data.push({
x: data.length,
y: Math.random() * 0.5 + 0.25 + Math.sin(offset * 0.1) * 0.15
});
}
offset++;
}
if (type === 'line' || type === 'training') {
type === 'training' ? drawTrainingChart() : drawLineChart();
} else {
drawBarChart();
}
requestAnimationFrame(animate);
};
resize();
animate();
}
}
customElements.define('chart-viz', ChartViz);