|
|
@@ -0,0 +1,176 @@
|
|
|
+const bulbListEl = document.getElementById('bulbList');
|
|
|
+const addBulbBtn = document.getElementById('addBulb');
|
|
|
+const removeBulbBtn = document.getElementById('removeBulb');
|
|
|
+
|
|
|
+const controls = document.getElementById('controls');
|
|
|
+const noSelect = document.getElementById('noSelect');
|
|
|
+
|
|
|
+const onOffSwitch = document.getElementById('onOffSwitch');
|
|
|
+const brightness = document.getElementById('brightness');
|
|
|
+const temperature = document.getElementById('temperature');
|
|
|
+
|
|
|
+const brightnessVal = document.getElementById('brightnessVal');
|
|
|
+const temperatureVal = document.getElementById('temperatureVal');
|
|
|
+
|
|
|
+const light = document.getElementById('light');
|
|
|
+const visualization = document.getElementById('visualization');
|
|
|
+
|
|
|
+function kelvinToRGB(kelvin) {
|
|
|
+ let temp = kelvin / 100;
|
|
|
+ let red, green, blue;
|
|
|
+
|
|
|
+ if (temp <= 66) {
|
|
|
+ red = 255;
|
|
|
+ } else {
|
|
|
+ red = 329.698727446 * Math.pow(temp - 60, -0.1332047592);
|
|
|
+ red = Math.min(Math.max(red, 0), 255);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (temp <= 66) {
|
|
|
+ green = 99.4708025861 * Math.log(temp) - 161.1195681661;
|
|
|
+ green = Math.min(Math.max(green, 0), 255);
|
|
|
+ } else {
|
|
|
+ green = 288.1221695283 * Math.pow(temp - 60, -0.0755148492);
|
|
|
+ green = Math.min(Math.max(green, 0), 255);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (temp >= 66) {
|
|
|
+ blue = 255;
|
|
|
+ } else if (temp <= 19) {
|
|
|
+ blue = 0;
|
|
|
+ } else {
|
|
|
+ blue = 138.5177312231 * Math.log(temp - 10) - 305.0447927307;
|
|
|
+ blue = Math.min(Math.max(blue, 0), 255);
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ red_return: Math.round(red),
|
|
|
+ green_return: Math.round(green),
|
|
|
+ blue_return: Math.round(blue)
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+let bulbs = [];
|
|
|
+let selectedBulb = null;
|
|
|
+
|
|
|
+function renderBulbList() {
|
|
|
+ bulbListEl.innerHTML = '';
|
|
|
+ bulbs.forEach((bulb, i) => {
|
|
|
+ const div = document.createElement('div');
|
|
|
+ div.className = 'bulb-item' + (selectedBulb === i ? ' selected' : '');
|
|
|
+ div.textContent = `Bulb ${i + 1}`;
|
|
|
+ div.onclick = () => selectBulb(i);
|
|
|
+ bulbListEl.appendChild(div);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function selectBulb(index) {
|
|
|
+ selectedBulb = index;
|
|
|
+ renderBulbList();
|
|
|
+ updateUI();
|
|
|
+}
|
|
|
+
|
|
|
+function setControlsEnabled(enabled) {
|
|
|
+ brightness.disabled = !enabled;
|
|
|
+ temperature.disabled = !enabled;
|
|
|
+ brightnessVal.style.opacity = enabled ? '1' : '0.5';
|
|
|
+ temperatureVal.style.opacity = enabled ? '1' : '0.5';
|
|
|
+}
|
|
|
+
|
|
|
+function updateUI() {
|
|
|
+ if (selectedBulb === null) {
|
|
|
+ controls.style.display = 'none';
|
|
|
+ noSelect.style.display = 'block';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const bulb = bulbs[selectedBulb];
|
|
|
+ noSelect.style.display = 'none';
|
|
|
+ controls.style.display = 'block';
|
|
|
+
|
|
|
+ onOffSwitch.checked = bulb.on;
|
|
|
+ brightness.value = bulb.brightness;
|
|
|
+ temperature.value = bulb.temperature;
|
|
|
+ brightnessVal.textContent = bulb.brightness;
|
|
|
+ temperatureVal.textContent = bulb.temperature;
|
|
|
+
|
|
|
+ setControlsEnabled(bulb.on);
|
|
|
+ updateVisualization();
|
|
|
+}
|
|
|
+
|
|
|
+function clamp01(v) {
|
|
|
+ return Math.max(0, Math.min(1, v));
|
|
|
+}
|
|
|
+
|
|
|
+function updateVisualization() {
|
|
|
+ if (selectedBulb === null) {
|
|
|
+ visualization.style.background = '#111';
|
|
|
+ light.style.background = 'radial-gradient(circle, #000 0%, #000 100%)';
|
|
|
+ light.style.boxShadow = 'none';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const bulb = bulbs[selectedBulb];
|
|
|
+
|
|
|
+ if (!bulb.on) {
|
|
|
+ visualization.style.background = '#111';
|
|
|
+ light.style.background = 'radial-gradient(circle, #000 0%, #000 100%)';
|
|
|
+ light.style.boxShadow = 'none';
|
|
|
+ light.style.opacity = '1';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const b = clamp01(bulb.brightness / 100);
|
|
|
+ const t = bulb.temperature;
|
|
|
+
|
|
|
+ let color_conversion = kelvinToRGB(t);
|
|
|
+ let color = `${color_conversion.red_return},${color_conversion.green_return},${color_conversion.blue_return}`;
|
|
|
+
|
|
|
+ const faceAlpha = 0.25 + b * 0.75;
|
|
|
+ const glowAlpha = 0.12 + b * 0.5;
|
|
|
+
|
|
|
+ light.style.background = `radial-gradient(circle at 50% 35%, rgba(${color}, ${faceAlpha}) 0%, rgba(${color}, ${Math.max(0.06, faceAlpha * 0.3)}) 35%, rgba(0,0,0,0) 75%)`;
|
|
|
+
|
|
|
+ const glowSize = 24 + b * 160;
|
|
|
+ light.style.boxShadow = `0 0 ${glowSize}px rgba(${color}, ${glowAlpha}), 0 0 ${Math.round(glowSize/2)}px rgba(${color}, ${glowAlpha / 2})`;
|
|
|
+
|
|
|
+ const roomGlowAlpha = Math.min(0.45, 0.06 + b * 0.25);
|
|
|
+ visualization.style.background = `radial-gradient(circle at 50% 10%, rgba(${color}, ${roomGlowAlpha}) 0%, rgba(0,0,0,0) 45%), #111`;
|
|
|
+ light.style.opacity = '1';
|
|
|
+}
|
|
|
+
|
|
|
+onOffSwitch.oninput = () => {
|
|
|
+ if (selectedBulb === null) return;
|
|
|
+ bulbs[selectedBulb].on = onOffSwitch.checked;
|
|
|
+ setControlsEnabled(bulbs[selectedBulb].on);
|
|
|
+ updateVisualization();
|
|
|
+};
|
|
|
+
|
|
|
+brightness.oninput = () => {
|
|
|
+ if (selectedBulb === null) return;
|
|
|
+ bulbs[selectedBulb].brightness = Number(brightness.value);
|
|
|
+ brightnessVal.textContent = brightness.value;
|
|
|
+ if (bulbs[selectedBulb].on) updateVisualization();
|
|
|
+};
|
|
|
+
|
|
|
+temperature.oninput = () => {
|
|
|
+ if (selectedBulb === null) return;
|
|
|
+ bulbs[selectedBulb].temperature = Number(temperature.value);
|
|
|
+ temperatureVal.textContent = temperature.value;
|
|
|
+ if (bulbs[selectedBulb].on) updateVisualization();
|
|
|
+};
|
|
|
+
|
|
|
+addBulbBtn.onclick = () => {
|
|
|
+ bulbs.push({ on: false, brightness: 50, temperature: 3000 });
|
|
|
+ renderBulbList();
|
|
|
+ selectBulb(bulbs.length - 1);
|
|
|
+};
|
|
|
+
|
|
|
+removeBulbBtn.onclick = () => {
|
|
|
+ if (selectedBulb === null) return;
|
|
|
+ bulbs.splice(selectedBulb, 1);
|
|
|
+ selectedBulb = null;
|
|
|
+ renderBulbList();
|
|
|
+ updateUI();
|
|
|
+};
|