Add global brightness control feature with socket handling and UI updates
This commit is contained in:
8
app.py
8
app.py
@@ -113,6 +113,14 @@ def handle_animation_speed_update(data):
|
||||
# Broadcast animation speed update to all clients
|
||||
emit('animation_speed_update', data, broadcast=True, include_self=False)
|
||||
|
||||
@socketio.on('brightness_update')
|
||||
def handle_brightness_update(data):
|
||||
"""Handle brightness updates from controller"""
|
||||
print(f'Brightness update received: {data}')
|
||||
print(f'Broadcasting brightness update to {len(connected_clients)} clients')
|
||||
# Broadcast brightness update to all clients
|
||||
emit('brightness_update', data, broadcast=True, include_self=False)
|
||||
|
||||
@socketio.on('join_room')
|
||||
def handle_join_room(data):
|
||||
"""Handle client joining a specific room"""
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1000px; /* 10 digits * 100px each */
|
||||
height: 1200px; /* 10 digits * 120px each (matches default wheel height) */
|
||||
transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
.digit {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
height: 120px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -143,64 +143,13 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.mechanical-sound::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, #ffffff, transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.digit-wheel.animating .mechanical-sound::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Glitch effect for mechanical feel */
|
||||
.digit-wheel.animating .digit.active {
|
||||
animation: glitch 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes glitch {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-1px); }
|
||||
75% { transform: translateX(1px); }
|
||||
}
|
||||
|
||||
/* Mechanical click sound simulation */
|
||||
.click-indicator {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #ffffff;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.digit-wheel.animating .click-indicator {
|
||||
animation: click 0.8s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes click {
|
||||
0% { opacity: 0; transform: translate(-50%, -50%) scale(0); }
|
||||
50% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
||||
100% { opacity: 0; transform: translate(-50%, -50%) scale(2); }
|
||||
}
|
||||
/* Removed visual effects that caused white lines during animation */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="status" class="status disconnected">Disconnected</div>
|
||||
|
||||
<div class="digit-wheel" id="digit0" data-position="0" style="left: 50px; top: 200px;">
|
||||
<div class="mechanical-sound"></div>
|
||||
<div class="click-indicator"></div>
|
||||
<div class="digit-strip" data-current="0">
|
||||
<div class="digit">0</div>
|
||||
<div class="digit">1</div>
|
||||
@@ -216,8 +165,6 @@
|
||||
</div>
|
||||
|
||||
<div class="digit-wheel" id="digit1" data-position="1" style="left: 150px; top: 200px;">
|
||||
<div class="mechanical-sound"></div>
|
||||
<div class="click-indicator"></div>
|
||||
<div class="digit-strip" data-current="0">
|
||||
<div class="digit">0</div>
|
||||
<div class="digit">1</div>
|
||||
@@ -233,8 +180,6 @@
|
||||
</div>
|
||||
|
||||
<div class="digit-wheel" id="digit2" data-position="2" style="left: 250px; top: 200px;">
|
||||
<div class="mechanical-sound"></div>
|
||||
<div class="click-indicator"></div>
|
||||
<div class="digit-strip" data-current="0">
|
||||
<div class="digit">0</div>
|
||||
<div class="digit">1</div>
|
||||
@@ -250,8 +195,6 @@
|
||||
</div>
|
||||
|
||||
<div class="digit-wheel" id="digit3" data-position="3" style="left: 350px; top: 200px;">
|
||||
<div class="mechanical-sound"></div>
|
||||
<div class="click-indicator"></div>
|
||||
<div class="digit-strip" data-current="0">
|
||||
<div class="digit">0</div>
|
||||
<div class="digit">1</div>
|
||||
@@ -267,8 +210,6 @@
|
||||
</div>
|
||||
|
||||
<div class="digit-wheel" id="digit4" data-position="4" style="left: 450px; top: 200px;">
|
||||
<div class="mechanical-sound"></div>
|
||||
<div class="click-indicator"></div>
|
||||
<div class="digit-strip" data-current="0">
|
||||
<div class="digit">0</div>
|
||||
<div class="digit">1</div>
|
||||
@@ -284,8 +225,6 @@
|
||||
</div>
|
||||
|
||||
<div class="digit-wheel" id="digit5" data-position="5" style="left: 550px; top: 200px;">
|
||||
<div class="mechanical-sound"></div>
|
||||
<div class="click-indicator"></div>
|
||||
<div class="digit-strip" data-current="0">
|
||||
<div class="digit">0</div>
|
||||
<div class="digit">1</div>
|
||||
@@ -320,6 +259,9 @@
|
||||
// Animation speed (default value)
|
||||
let currentAnimationSpeed = 0.5;
|
||||
|
||||
// Brightness (default value)
|
||||
let currentBrightness = 100;
|
||||
|
||||
// Connection status
|
||||
socket.on('connect', function() {
|
||||
statusDiv.textContent = 'Connected';
|
||||
@@ -372,6 +314,12 @@
|
||||
updateAnimationSpeed(data.speed || data);
|
||||
});
|
||||
|
||||
// Handle brightness updates from controller
|
||||
socket.on('brightness_update', function(data) {
|
||||
console.log('Received brightness update:', data);
|
||||
updateBrightness(data.brightness || data);
|
||||
});
|
||||
|
||||
// Handle general events (for backward compatibility)
|
||||
socket.on('receive_event', function(data) {
|
||||
console.log('Received event:', data);
|
||||
@@ -390,6 +338,9 @@
|
||||
if (data.type === 'animation_speed' || data.speed) {
|
||||
updateAnimationSpeed(data.speed || data);
|
||||
}
|
||||
if (data.type === 'brightness' || data.brightness) {
|
||||
updateBrightness(data.brightness || data);
|
||||
}
|
||||
});
|
||||
|
||||
function updateCounter(values) {
|
||||
@@ -450,6 +401,7 @@
|
||||
const currentValue = parseInt(strip.getAttribute('data-current') || '0');
|
||||
const targetY = -(currentValue * height);
|
||||
strip.style.transform = `translateY(${targetY}px)`;
|
||||
console.log(`Recalculated digit strip ${index} position to ${targetY}px for value ${currentValue}`);
|
||||
});
|
||||
|
||||
// Update individual digit heights and widths
|
||||
@@ -473,6 +425,21 @@
|
||||
});
|
||||
}
|
||||
|
||||
function updateBrightness(brightness) {
|
||||
console.log('Updating brightness to:', brightness);
|
||||
currentBrightness = brightness;
|
||||
|
||||
// Calculate brightness filter value (0-1 range)
|
||||
const brightnessFilter = brightness / 100;
|
||||
|
||||
// Update all digit wheels with new brightness
|
||||
const digitWheels = document.querySelectorAll('.digit-wheel');
|
||||
digitWheels.forEach((wheel, index) => {
|
||||
wheel.style.filter = `brightness(${brightnessFilter})`;
|
||||
console.log(`Updated digit wheel ${index} brightness to ${brightness}% (filter: ${brightnessFilter})`);
|
||||
});
|
||||
}
|
||||
|
||||
function animateDigitWheel(position, fromValue, toValue) {
|
||||
const wheel = digitWheels[position];
|
||||
const strip = wheel.querySelector('.digit-strip');
|
||||
@@ -485,6 +452,8 @@
|
||||
// Calculate the target position using current wheel height
|
||||
const targetY = -(toValue * wheelHeight);
|
||||
|
||||
console.log(`Animating digit ${position} from ${fromValue} to ${toValue}, targetY: ${targetY}, wheelHeight: ${wheelHeight}`);
|
||||
|
||||
// Add animation class
|
||||
wheel.classList.add('animating');
|
||||
|
||||
@@ -498,9 +467,14 @@
|
||||
digit.classList.toggle('active', index === toValue);
|
||||
});
|
||||
|
||||
// Remove animation class after animation completes
|
||||
// Remove animation class after animation completes and ensure proper positioning
|
||||
setTimeout(() => {
|
||||
wheel.classList.remove('animating');
|
||||
|
||||
// Double-check the position is correct after animation
|
||||
const finalTargetY = -(toValue * wheelHeight);
|
||||
strip.style.transform = `translateY(${finalTargetY}px)`;
|
||||
console.log(`Final position check for digit ${position}: ${finalTargetY}px`);
|
||||
}, currentAnimationSpeed * 1000);
|
||||
}
|
||||
|
||||
@@ -543,36 +517,7 @@
|
||||
socket.emit('viewport_update', viewportData);
|
||||
}
|
||||
|
||||
// Add some mechanical sound simulation
|
||||
function playMechanicalSound() {
|
||||
// Create a simple mechanical click sound using Web Audio API
|
||||
try {
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
|
||||
oscillator.frequency.exponentialRampToValueAtTime(400, audioContext.currentTime + 0.1);
|
||||
|
||||
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
|
||||
|
||||
oscillator.start(audioContext.currentTime);
|
||||
oscillator.stop(audioContext.currentTime + 0.1);
|
||||
} catch (e) {
|
||||
console.log('Audio not supported');
|
||||
}
|
||||
}
|
||||
|
||||
// Play sound when digits animate
|
||||
document.addEventListener('animationstart', function(e) {
|
||||
if (e.target.classList.contains('digit-wheel')) {
|
||||
playMechanicalSound();
|
||||
}
|
||||
});
|
||||
// Removed mechanical sound simulation to eliminate white lines during animation
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -643,6 +643,102 @@
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.brightness-controls {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #00ff00;
|
||||
}
|
||||
|
||||
.brightness-controls h3 {
|
||||
color: #00ff00;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.brightness-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.brightness-label {
|
||||
font-weight: bold;
|
||||
color: #00ff00;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.brightness-slider {
|
||||
flex: 1;
|
||||
max-width: 300px;
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.brightness-slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: #00ff00;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.brightness-slider::-moz-range-thumb {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: #00ff00;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.brightness-value {
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
color: #00ff00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.brightness-preview {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border: 1px solid #00ff00;
|
||||
border-radius: 5px;
|
||||
margin-left: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.brightness-preview-box {
|
||||
width: 40px;
|
||||
height: 60px;
|
||||
background: linear-gradient(145deg, #2a2a2a, #1a1a1a);
|
||||
border: 1px solid #444;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.brightness-preview-digit {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
font-size: 20px;
|
||||
background: linear-gradient(145deg, #1a1a1a, #0a0a0a);
|
||||
}
|
||||
|
||||
.counter-preview {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
@@ -730,6 +826,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="brightness-controls">
|
||||
<h3>Global Brightness Control</h3>
|
||||
<div class="brightness-group">
|
||||
<div class="brightness-label">Brightness:</div>
|
||||
<input type="range" class="brightness-slider" id="globalBrightness" min="0" max="100" value="100" step="5">
|
||||
<span class="brightness-value" id="globalBrightnessValue">100%</span>
|
||||
<div class="brightness-preview">
|
||||
<div class="brightness-preview-box" id="brightnessPreviewBox">
|
||||
<div class="brightness-preview-digit" id="brightnessPreviewDigit">8</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<button class="btn btn-primary" onclick="updateGlobalBrightness()">Update All Wheels</button>
|
||||
<button class="btn btn-secondary" onclick="resetGlobalBrightness()">Reset to Default</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wheel-size-controls">
|
||||
<h3>Digit Wheel Size</h3>
|
||||
<div class="wheel-size-group">
|
||||
@@ -964,6 +1078,9 @@
|
||||
// Animation speed (default value)
|
||||
let currentAnimationSpeed = 0.5;
|
||||
|
||||
// Brightness (default value)
|
||||
let currentBrightness = 100;
|
||||
|
||||
// Connection status
|
||||
socket.on('connect', function() {
|
||||
statusDiv.textContent = 'Connected';
|
||||
@@ -1071,6 +1188,21 @@
|
||||
speedPreviewStrip.style.transition = `transform ${newSpeed}s ease`;
|
||||
});
|
||||
|
||||
// Add event listener to brightness slider
|
||||
const brightnessSlider = document.getElementById('globalBrightness');
|
||||
const brightnessValue = document.getElementById('globalBrightnessValue');
|
||||
const brightnessPreviewBox = document.getElementById('brightnessPreviewBox');
|
||||
|
||||
brightnessSlider.addEventListener('input', function() {
|
||||
const newBrightness = parseInt(this.value);
|
||||
currentBrightness = newBrightness;
|
||||
brightnessValue.textContent = newBrightness + '%';
|
||||
|
||||
// Update preview with brightness filter
|
||||
const brightnessFilter = newBrightness / 100;
|
||||
brightnessPreviewBox.style.filter = `brightness(${brightnessFilter})`;
|
||||
});
|
||||
|
||||
function updateCounter() {
|
||||
const counterData = {
|
||||
type: 'counter_update',
|
||||
@@ -1300,6 +1432,27 @@
|
||||
}, (currentAnimationSpeed * 1000) + 200);
|
||||
}
|
||||
|
||||
function updateGlobalBrightness() {
|
||||
const brightnessData = {
|
||||
type: 'brightness_update',
|
||||
brightness: currentBrightness,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
console.log('Sending brightness update:', brightnessData);
|
||||
socket.emit('brightness_update', brightnessData);
|
||||
addSentEvent(brightnessData);
|
||||
}
|
||||
|
||||
function resetGlobalBrightness() {
|
||||
const defaultBrightness = 100;
|
||||
brightnessSlider.value = defaultBrightness;
|
||||
currentBrightness = defaultBrightness;
|
||||
brightnessValue.textContent = defaultBrightness + '%';
|
||||
brightnessPreviewBox.style.filter = 'brightness(1)';
|
||||
updateGlobalBrightness();
|
||||
}
|
||||
|
||||
function incrementDigit(index) {
|
||||
let currentValue = currentValues[index];
|
||||
currentValue = (currentValue + 1) % 10; // Wrap around from 9 to 0
|
||||
|
||||
Reference in New Issue
Block a user