web chart - 실시간 차트
- Download
- real-time-chart.html(4.8 KB) 07-30 16:330
- Link
- https://winviva.kr/homepage.html0
- https://claude.site/artifacts/beb1aa11-1533-4865-b1d3-706dbde505eb0
실시간차트
axㅁㅁㅁㅁ
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>실시간 인터랙티브 차트</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.css" rel="stylesheet">
<style>
#chart {
width: 100%;
height: 500px;
}
.annotation {
position: absolute;
background: white;
border: 1px solid black;
padding: 5px;
display: none;
}
.selection {
fill: rgba(0, 0, 255, 0.1);
stroke: blue;
}
</style>
</head>
<body>
<div id="chart"></div>
<div id="annotation" class="annotation">
<textarea id="annotationText" rows="3" cols="30"></textarea>
<button onclick="saveAnnotation()">저장</button>
</div>
<script>
let chart;
let data = [];
let annotations = [];
let selectedRange = null;
function initChart() {
chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['data', ...data]
]
},
axis: {
y: {
padding: {
top: 100,
bottom: 100
}
}
},
zoom: {
enabled: true
},
subchart: {
show: true
}
});
d3.select('#chart svg')
.call(d3.drag().on("drag", dragged))
.on("wheel", zoomed)
.on("click", clicked);
d3.select('#chart svg')
.append("rect")
.attr("class", "selection")
.style("display", "none");
}
function updateChart() {
const newValue = Math.random() * 100;
data.push(newValue);
if (data.length > 100) data.shift();
chart.load({
columns: [
['data', ...data]
]
});
updateAnnotations();
setTimeout(updateChart, 1000);
}
function dragged(event) {
const dx = d3.event.dx;
const range = chart.x.range();
const domainDiff = chart.x.invert(range[1]) - chart.x.invert(range[0]);
const domainDx = (domainDiff / (range[1] - range[0])) * dx;
chart.zoom.translate([chart.zoom.translate()[0] - domainDx, 0]);
chart.zoom.update();
}
function zoomed(event) {
const direction = d3.event.deltaY < 0 ? 'in' : 'out';
const factor = direction === 'in' ? 1.1 : 0.9;
chart.zoom.scale(chart.zoom.scale() * factor);
chart.zoom.update();
}
function clicked(event) {
const [x, y] = d3.mouse(this);
const xValue = chart.x.invert(x);
const yValue = chart.y.invert(y);
const annotation = document.getElementById('annotation');
annotation.style.left = `${x}px`;
annotation.style.top = `${y}px`;
annotation.style.display = 'block';
selectedRange = { x: xValue, y: yValue };
}
function saveAnnotation() {
const text = document.getElementById('annotationText').value;
annotations.push({ ...selectedRange, text });
document.getElementById('annotation').style.display = 'none';
document.getElementById('annotationText').value = '';
updateAnnotations();
}
function updateAnnotations() {
d3.selectAll('.annotation-point').remove();
d3.selectAll('.annotation-tooltip').remove();
annotations.forEach(ann => {
const x = chart.x(ann.x);
const y = chart.y(ann.y);
d3.select('#chart svg')
.append('circle')
.attr('class', 'annotation-point')
.attr('cx', x)
.attr('cy', y)
.attr('r', 5)
.attr('fill', 'red');
d3.select('#chart svg')
.append('text')
.attr('class', 'annotation-tooltip')
.attr('x', x + 10)
.attr('y', y - 10)
.text(ann.text);
});
}
initChart();
updateChart();
</script>
</body>
</html>