optimizations for mobile
This commit is contained in:
@@ -11,7 +11,11 @@
|
||||
}>();
|
||||
|
||||
let particles: Particle[] = [];
|
||||
const numParticles = 40; // Reduced from 60 for better performance with two visualizations
|
||||
// Detect mobile for performance optimization
|
||||
const isMobile = typeof window !== 'undefined' &&
|
||||
(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
|
||||
window.innerWidth < 768);
|
||||
const numParticles = isMobile ? 15 : 40; // Significantly reduced for mobile
|
||||
let audioData: Float32Array | null = null;
|
||||
|
||||
// Cache frequently used calculations
|
||||
@@ -19,6 +23,10 @@
|
||||
let midRange = 0;
|
||||
let trebleStart = 0;
|
||||
|
||||
// Track if user is scrolling to reduce load
|
||||
let isScrolling = false;
|
||||
let scrollTimeout: number;
|
||||
|
||||
class Particle {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -42,13 +50,14 @@
|
||||
this.alpha = p.random(100, 255);
|
||||
}
|
||||
|
||||
update(p: p5, audioLevel: number, bass: number, mid: number) {
|
||||
update(p: p5, audioLevel: number, bass: number, mid: number, isMobile: boolean) {
|
||||
// Base movement - gentle constant speed
|
||||
this.x += this.vx;
|
||||
this.y += this.vy;
|
||||
|
||||
// Subtle audio reactive displacement (only every 3rd frame for performance)
|
||||
if (p.frameCount % 3 === 0) {
|
||||
// Subtle audio reactive displacement (less frequent on mobile)
|
||||
const updateFrequency = isMobile ? 5 : 3;
|
||||
if (p.frameCount % updateFrequency === 0) {
|
||||
const displacement = audioLevel * 20;
|
||||
const angle = p.noise(this.x * 0.01, this.y * 0.01, p.frameCount * 0.01) * p.TWO_PI;
|
||||
this.x += p.cos(angle) * displacement * 0.05;
|
||||
@@ -86,7 +95,8 @@
|
||||
const sketch = (p: p5) => {
|
||||
p.setup = () => {
|
||||
p.createCanvas(width, height);
|
||||
p.frameRate(30); // Reduce from 60fps to 30fps for better performance
|
||||
// Lower framerate on mobile for better performance
|
||||
p.frameRate(isMobile ? 20 : 30);
|
||||
p.background(0);
|
||||
|
||||
// Initialize particles
|
||||
@@ -94,11 +104,22 @@
|
||||
for (let i = 0; i < numParticles; i++) {
|
||||
particles.push(new Particle(p, i));
|
||||
}
|
||||
|
||||
// Handle scroll events to pause visualization
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('scroll', () => {
|
||||
isScrolling = true;
|
||||
clearTimeout(scrollTimeout);
|
||||
scrollTimeout = window.setTimeout(() => {
|
||||
isScrolling = false;
|
||||
}, 150);
|
||||
}, { passive: true });
|
||||
}
|
||||
};
|
||||
|
||||
p.draw = () => {
|
||||
// Pause visualization when document is hidden (tab not focused)
|
||||
if (document.hidden) {
|
||||
// Pause visualization when document is hidden, scrolling, or on mobile during heavy load
|
||||
if (document.hidden || (isMobile && isScrolling)) {
|
||||
return;
|
||||
}
|
||||
p.background(0, 30); // Fade effect
|
||||
@@ -153,12 +174,12 @@
|
||||
|
||||
// Update and display particles
|
||||
for (let i = 0; i < particles.length; i++) {
|
||||
particles[i].update(p, audioLevel, bass, mid);
|
||||
particles[i].update(p, audioLevel, bass, mid, isMobile);
|
||||
particles[i].display(p, audioLevel);
|
||||
}
|
||||
|
||||
// Draw connections less frequently (every other frame) for better performance
|
||||
if (p.frameCount % 2 === 0) {
|
||||
// Draw connections - skip on mobile for better performance
|
||||
if (!isMobile && p.frameCount % 2 === 0) {
|
||||
for (let i = 0; i < particles.length; i++) {
|
||||
// Only check next 5 particles instead of all, reduces O(n²) significantly
|
||||
for (let j = i + 1; j < Math.min(i + 6, particles.length); j++) {
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
let isPlaying = $state(false);
|
||||
let isInitialized = $state(false);
|
||||
|
||||
// Detect mobile for visualization sizing
|
||||
const isMobile = typeof window !== 'undefined' &&
|
||||
(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
|
||||
window.innerWidth < 768);
|
||||
const vizSize = isMobile ? 280 : 400;
|
||||
|
||||
// Audio components
|
||||
let noiseSynth: Tone.NoiseSynth | null = null;
|
||||
let reverb: Tone.Reverb | null = null;
|
||||
@@ -192,6 +198,26 @@
|
||||
// Lifecycle
|
||||
onMount(() => {
|
||||
initializeAudio();
|
||||
|
||||
// Handle page visibility changes (screen sleep/wake, tab switching)
|
||||
const handleVisibilityChange = async () => {
|
||||
if (!document.hidden && isPlaying) {
|
||||
// Page is visible again - resume audio context if suspended
|
||||
if (Tone.getContext().state === 'suspended') {
|
||||
await Tone.getContext().resume();
|
||||
}
|
||||
// Restart loop if it stopped
|
||||
if (loop && loop.state !== 'started') {
|
||||
loop.start(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -243,6 +269,6 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<AudioVisualization {isPlaying} {analyser} width={400} height={400} />
|
||||
<AudioVisualization {isPlaying} {analyser} width={vizSize} height={vizSize} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,6 +31,12 @@
|
||||
let isPlaying = $state(false);
|
||||
let isInitialized = $state(false);
|
||||
|
||||
// Detect mobile for visualization sizing
|
||||
const isMobile = typeof window !== 'undefined' &&
|
||||
(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
|
||||
window.innerWidth < 768);
|
||||
const vizSize = isMobile ? 280 : 400;
|
||||
|
||||
// Audio components
|
||||
let synth: Tone.PolySynth | null = null;
|
||||
let arpSynth: Tone.Synth | null = null;
|
||||
@@ -397,6 +403,26 @@
|
||||
// Lifecycle
|
||||
onMount(() => {
|
||||
initializeAudio();
|
||||
|
||||
// Handle page visibility changes (screen sleep/wake, tab switching)
|
||||
const handleVisibilityChange = async () => {
|
||||
if (!document.hidden && isPlaying) {
|
||||
// Page is visible again - resume audio context if suspended
|
||||
if (Tone.getContext().state === 'suspended') {
|
||||
await Tone.getContext().resume();
|
||||
}
|
||||
// Restart transport if it stopped
|
||||
if (Tone.getTransport().state !== 'started') {
|
||||
Tone.getTransport().start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -471,6 +497,6 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<AudioVisualization {isPlaying} {analyser} width={400} height={400} />
|
||||
<AudioVisualization {isPlaying} {analyser} width={vizSize} height={vizSize} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user