IMP: toggle and blended mode for visualizer
This commit is contained in:
parent
631fdc276e
commit
35080751f9
4 changed files with 87 additions and 11 deletions
|
|
@ -447,6 +447,38 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
|||
});
|
||||
}
|
||||
|
||||
// Visualizer Enabled Toggle
|
||||
const visualizerEnabledToggle = document.getElementById('visualizer-enabled-toggle');
|
||||
const visualizerModeSetting = document.getElementById('visualizer-mode-setting');
|
||||
const visualizerSmartIntensitySetting = document.getElementById('visualizer-smart-intensity-setting');
|
||||
const visualizerSensitivitySetting = document.getElementById('visualizer-sensitivity-setting');
|
||||
|
||||
const updateVisualizerSettingsVisibility = (enabled) => {
|
||||
const display = enabled ? 'flex' : 'none';
|
||||
if (visualizerModeSetting) visualizerModeSetting.style.display = display;
|
||||
if (visualizerSmartIntensitySetting) visualizerSmartIntensitySetting.style.display = display;
|
||||
if (visualizerSensitivitySetting) visualizerSensitivitySetting.style.display = display;
|
||||
};
|
||||
|
||||
if (visualizerEnabledToggle) {
|
||||
visualizerEnabledToggle.checked = visualizerSettings.isEnabled();
|
||||
updateVisualizerSettingsVisibility(visualizerEnabledToggle.checked);
|
||||
|
||||
visualizerEnabledToggle.addEventListener('change', (e) => {
|
||||
visualizerSettings.setEnabled(e.target.checked);
|
||||
updateVisualizerSettingsVisibility(e.target.checked);
|
||||
});
|
||||
}
|
||||
|
||||
// Visualizer Mode Select
|
||||
const visualizerModeSelect = document.getElementById('visualizer-mode-select');
|
||||
if (visualizerModeSelect) {
|
||||
visualizerModeSelect.value = visualizerSettings.getMode();
|
||||
visualizerModeSelect.addEventListener('change', (e) => {
|
||||
visualizerSettings.setMode(e.target.value);
|
||||
});
|
||||
}
|
||||
|
||||
// Filename template setting
|
||||
const filenameTemplate = document.getElementById('filename-template');
|
||||
if (filenameTemplate) {
|
||||
|
|
|
|||
|
|
@ -624,6 +624,33 @@ export const bulkDownloadSettings = {
|
|||
export const visualizerSettings = {
|
||||
SENSITIVITY_KEY: 'visualizer-sensitivity',
|
||||
SMART_INTENSITY_KEY: 'visualizer-smart-intensity',
|
||||
ENABLED_KEY: 'visualizer-enabled',
|
||||
MODE_KEY: 'visualizer-mode', // 'solid' or 'blended'
|
||||
|
||||
isEnabled() {
|
||||
try {
|
||||
const val = localStorage.getItem(this.ENABLED_KEY);
|
||||
return val === null ? true : val === 'true';
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
setEnabled(enabled) {
|
||||
localStorage.setItem(this.ENABLED_KEY, enabled);
|
||||
},
|
||||
|
||||
getMode() {
|
||||
try {
|
||||
return localStorage.getItem(this.MODE_KEY) || 'solid';
|
||||
} catch {
|
||||
return 'solid';
|
||||
}
|
||||
},
|
||||
|
||||
setMode(mode) {
|
||||
localStorage.setItem(this.MODE_KEY, mode);
|
||||
},
|
||||
|
||||
getSensitivity() {
|
||||
try {
|
||||
|
|
|
|||
7
js/ui.js
7
js/ui.js
|
|
@ -17,7 +17,7 @@ import {
|
|||
escapeHtml,
|
||||
} from './utils.js';
|
||||
import { openLyricsPanel } from './lyrics.js';
|
||||
import { recentActivityManager, backgroundSettings, cardSettings } from './storage.js';
|
||||
import { recentActivityManager, backgroundSettings, cardSettings, visualizerSettings } from './storage.js';
|
||||
import { db } from './db.js';
|
||||
import { getVibrantColorFromImage } from './vibrant-color.js';
|
||||
import { syncManager } from './accounts/pocketbase.js';
|
||||
|
|
@ -748,6 +748,11 @@ export class UIRenderer {
|
|||
overlay.style.display = 'flex';
|
||||
|
||||
const startVisualizer = () => {
|
||||
if (!visualizerSettings.isEnabled()) {
|
||||
if (this.visualizer) this.visualizer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.visualizer && audioPlayer) {
|
||||
const canvas = document.getElementById('visualizer-canvas');
|
||||
if (canvas) {
|
||||
|
|
|
|||
|
|
@ -82,18 +82,28 @@ export class Visualizer {
|
|||
const ctx = this.ctx;
|
||||
|
||||
let sensitivity = visualizerSettings.getSensitivity();
|
||||
ctx.fillStyle = 'rgba(10, 10, 10, 0.3)';
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
const mode = visualizerSettings.getMode();
|
||||
const isDark = document.documentElement.getAttribute('data-theme') !== 'light';
|
||||
|
||||
if (mode === 'blended') {
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
} else {
|
||||
// Match background to theme if in solid mode
|
||||
if (isDark) {
|
||||
ctx.fillStyle = 'rgba(10, 10, 10, 0.3)';
|
||||
} else {
|
||||
ctx.fillStyle = 'rgba(240, 240, 240, 0.3)';
|
||||
}
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
}
|
||||
|
||||
const bufferLength = this.analyser.frequencyBinCount;
|
||||
const dataArray = new Uint8Array(bufferLength);
|
||||
this.analyser.getByteFrequencyData(dataArray);
|
||||
|
||||
let bassSum = 0;
|
||||
|
||||
for (let i = 0; i < 4; i++) bassSum += dataArray[i];
|
||||
const bass = bassSum / 4 / 255;
|
||||
|
||||
const intensity = bass * bass;
|
||||
|
||||
this.energyAverage = this.energyAverage * 0.99 + intensity * 0.01;
|
||||
|
|
@ -116,7 +126,6 @@ export class Visualizer {
|
|||
}
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
if (intensity > threshold) {
|
||||
if (intensity > this.lastIntensity + 0.05 && now - this.lastBeatTime > 50) {
|
||||
this.kick = 1.0;
|
||||
|
|
@ -158,7 +167,6 @@ export class Visualizer {
|
|||
y: Math.random() * h,
|
||||
vx: (Math.random() - 0.5) * 2,
|
||||
vy: (Math.random() - 0.5) * 2,
|
||||
size: Math.random() * 3 + 1,
|
||||
baseSize: Math.random() * 3 + 1,
|
||||
});
|
||||
}
|
||||
|
|
@ -170,6 +178,9 @@ export class Visualizer {
|
|||
ctx.fillStyle = primaryColor;
|
||||
ctx.strokeStyle = primaryColor;
|
||||
|
||||
const maxDist = 150 + intensity * 50 + this.kick * 50 * sensitivity;
|
||||
const maxDistSq = maxDist * maxDist;
|
||||
|
||||
for (let i = 0; i < this.particles.length; i++) {
|
||||
let p = this.particles[i];
|
||||
|
||||
|
|
@ -197,13 +208,14 @@ export class Visualizer {
|
|||
const p2 = this.particles[j];
|
||||
const dx = p.x - p2.x;
|
||||
const dy = p.y - p2.y;
|
||||
|
||||
// Optimization: Early exit for x distance
|
||||
if (Math.abs(dx) > maxDist) continue;
|
||||
|
||||
const distSq = dx * dx + dy * dy;
|
||||
|
||||
const maxDist = 150 + intensity * 50 + this.kick * 50 * sensitivity;
|
||||
const maxDistSq = maxDist * maxDist;
|
||||
|
||||
if (distSq < maxDistSq) {
|
||||
const dist = Math.sqrt(distSq);
|
||||
const dist = Math.sqrt(distSq); // Still need dist for alpha/linewidth, but now we only sqrt when necessary
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = (1 - dist / maxDist) * (1 + this.kick * 1.5 * sensitivity);
|
||||
ctx.globalAlpha = (1 - dist / maxDist) * (0.3 + intensity * 0.2 + this.kick * 0.3 * sensitivity);
|
||||
|
|
|
|||
Loading…
Reference in a new issue