first
This commit is contained in:
273
ascii-bar-chart.html
Normal file
273
ascii-bar-chart.html
Normal file
@@ -0,0 +1,273 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ASCII Bar Chart</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #1e1e1e;
|
||||
color: #00ff00;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#chart-container {
|
||||
background-color: #000;
|
||||
padding: 20px;
|
||||
border: 2px solid #00ff00;
|
||||
border-radius: 5px;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
font-size: 8px;
|
||||
line-height: 1.0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#controls {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #00ff00;
|
||||
color: #000;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #00cc00;
|
||||
}
|
||||
|
||||
#info {
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#chart-info {
|
||||
margin-top: 15px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
#chart-info div {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#settings {
|
||||
background-color: #2a2a2a;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #00ff00;
|
||||
}
|
||||
|
||||
#settings input {
|
||||
background-color: #1e1e1e;
|
||||
color: #00ff00;
|
||||
border: 1px solid #00ff00;
|
||||
padding: 3px 5px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
#settings label {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ASCII Vertical Bar Chart</h1>
|
||||
|
||||
<div id="controls">
|
||||
<button onclick="dataGenerator.start()">Start</button>
|
||||
<button onclick="dataGenerator.stop()">Stop</button>
|
||||
<button onclick="dataGenerator.reset()">Reset</button>
|
||||
</div>
|
||||
|
||||
<div id="settings" style="margin-bottom: 15px; font-size: 12px;">
|
||||
<label>
|
||||
Update Interval (ms):
|
||||
<input type="number" id="update-interval" value="1000" min="100" max="10000" step="100" style="width: 80px;">
|
||||
</label>
|
||||
<label style="margin-left: 15px;">
|
||||
Max Columns:
|
||||
<input type="number" id="max-columns" value="30" min="10" max="100" step="5" style="width: 60px;">
|
||||
</label>
|
||||
<label style="margin-left: 15px;">
|
||||
Chart Height:
|
||||
<input type="number" id="chart-height" value="20" min="10" max="50" step="5" style="width: 60px;">
|
||||
</label>
|
||||
<button onclick="applySettings()" style="margin-left: 15px; padding: 5px 15px;">Apply Settings</button>
|
||||
</div>
|
||||
|
||||
<div id="info">
|
||||
<span>Data points: <span id="count">0</span></span> |
|
||||
<span>Status: <span id="status">Stopped</span></span> |
|
||||
<span>Next update in: <span id="countdown">--</span>s</span>
|
||||
</div>
|
||||
|
||||
<div id="chart-container"></div>
|
||||
|
||||
<div id="chart-info">
|
||||
<div><strong>Legend:</strong> Each X represents a count unit</div>
|
||||
<div><strong>Values:</strong> <span id="values">--</span></div>
|
||||
<div><strong>Max value:</strong> <span id="max-value">--</span>, <strong>Scale:</strong> <span id="scale">--</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Include the ASCII Bar Chart library -->
|
||||
<script src="ascii-bar-chart.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
// Initialize the chart
|
||||
let chart = new ASCIIBarChart('chart-container', {
|
||||
maxHeight: 20,
|
||||
maxDataPoints: 30,
|
||||
title: 'Real-Time Data Visualization',
|
||||
xAxisLabel: 'Time (seconds)',
|
||||
yAxisLabel: 'Count',
|
||||
autoFitWidth: true // Automatically adjust font size to fit container width
|
||||
});
|
||||
|
||||
// Initial render
|
||||
chart.render();
|
||||
chart.updateInfo();
|
||||
|
||||
// Test data generator (separate from chart API)
|
||||
class DataGenerator {
|
||||
constructor(updateInterval = 1000) {
|
||||
this.baseValue = 10;
|
||||
this.intervalId = null;
|
||||
this.countdownId = null;
|
||||
this.nextUpdateTime = null;
|
||||
this.isRunning = false;
|
||||
this.updateInterval = updateInterval;
|
||||
}
|
||||
|
||||
setUpdateInterval(interval) {
|
||||
this.updateInterval = interval;
|
||||
// If running, restart with new interval
|
||||
if (this.isRunning) {
|
||||
this.stop();
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
generateValue() {
|
||||
// Add some randomness around the base value (±3)
|
||||
const variation = Math.floor(Math.random() * 7) - 3;
|
||||
const value = this.baseValue + variation;
|
||||
|
||||
// Increase the base value slightly for next time (0.5 to 1.5 increase)
|
||||
this.baseValue += 0.5 + Math.random();
|
||||
|
||||
return Math.max(1, Math.round(value));
|
||||
}
|
||||
|
||||
updateCountdown() {
|
||||
if (!this.isRunning || !this.nextUpdateTime) {
|
||||
document.getElementById('countdown').textContent = '--';
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const remaining = Math.max(0, Math.ceil((this.nextUpdateTime - now) / 1000));
|
||||
document.getElementById('countdown').textContent = remaining;
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this.isRunning) return;
|
||||
|
||||
this.isRunning = true;
|
||||
document.getElementById('status').textContent = 'Running';
|
||||
|
||||
// Add first data point immediately
|
||||
chart.addValue(this.generateValue());
|
||||
|
||||
// Set up interval for subsequent updates
|
||||
this.nextUpdateTime = Date.now() + this.updateInterval;
|
||||
this.intervalId = setInterval(() => {
|
||||
chart.addValue(this.generateValue());
|
||||
this.nextUpdateTime = Date.now() + this.updateInterval;
|
||||
}, this.updateInterval);
|
||||
|
||||
// Update countdown every second
|
||||
this.countdownId = setInterval(() => {
|
||||
this.updateCountdown();
|
||||
}, 1000);
|
||||
|
||||
this.updateCountdown();
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this.isRunning) return;
|
||||
|
||||
this.isRunning = false;
|
||||
document.getElementById('status').textContent = 'Stopped';
|
||||
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
}
|
||||
|
||||
if (this.countdownId) {
|
||||
clearInterval(this.countdownId);
|
||||
this.countdownId = null;
|
||||
}
|
||||
|
||||
this.nextUpdateTime = null;
|
||||
this.updateCountdown();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.stop();
|
||||
this.baseValue = 10;
|
||||
chart.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Create data generator for testing
|
||||
let dataGenerator = new DataGenerator(1000);
|
||||
|
||||
// Function to apply settings
|
||||
function applySettings() {
|
||||
const wasRunning = dataGenerator.isRunning;
|
||||
|
||||
// Stop current generator
|
||||
dataGenerator.stop();
|
||||
|
||||
// Get new settings
|
||||
const updateInterval = parseInt(document.getElementById('update-interval').value);
|
||||
const maxColumns = parseInt(document.getElementById('max-columns').value);
|
||||
const chartHeight = parseInt(document.getElementById('chart-height').value);
|
||||
|
||||
// Recreate chart with new settings
|
||||
chart = new ASCIIBarChart('chart-container', {
|
||||
maxHeight: chartHeight,
|
||||
maxDataPoints: maxColumns,
|
||||
title: 'Real-Time Data Visualization',
|
||||
xAxisLabel: 'Time (seconds)',
|
||||
yAxisLabel: 'Count',
|
||||
autoFitWidth: true
|
||||
});
|
||||
|
||||
chart.render();
|
||||
chart.updateInfo();
|
||||
|
||||
// Recreate data generator with new interval
|
||||
dataGenerator = new DataGenerator(updateInterval);
|
||||
|
||||
// Restart if it was running
|
||||
if (wasRunning) {
|
||||
dataGenerator.start();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user