Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ const AnimatedBackground = React.memo(({
useEffect(() => {
const animation = setupCanvas();
let lastTime = 0;
const frameInterval = 1000 / fps;
let lastEvalTime = 0;
let currentFps = fps;

const loop = (currentTime) => {
// Check animation controls
Expand All @@ -250,6 +251,28 @@ const AnimatedBackground = React.memo(({

animationRef.current = requestAnimationFrame(loop);

// Adaptive performance adjustments
if (adaptivePerformance && performanceMonitor) {
const { avgFps, performanceLevel } = performanceMonitor;

// Only evaluate adaptive adjustments once per second to prevent rapid toggling
// and console spamming
if (currentTime - lastEvalTime > 1000) {
lastEvalTime = currentTime;
if (performanceLevel === 'poor' && avgFps > 0 && avgFps < 25) {
if (currentFps > 15) {
// Automatically reduce fps to improve rendering stability
currentFps = Math.max(15, currentFps - 5);
console.warn(`Poor performance detected. Automatically reduced target FPS to ${currentFps}.`);
}
} else if (performanceLevel === 'excellent' && avgFps > 55 && currentFps < fps) {
// Gradually restore fps if performance is excellent
currentFps = Math.min(fps, currentFps + 1);
}
}
}

const frameInterval = 1000 / currentFps;
const deltaTime = currentTime - lastTime;
const speedMultiplier = animationControls ? animationControls.speed : 1;
const adjustedFrameInterval = frameInterval / speedMultiplier;
Expand All @@ -263,15 +286,6 @@ const AnimatedBackground = React.memo(({
}

animation();

// Adaptive performance adjustments
if (adaptivePerformance && performanceMonitor) {
const { avgFps, performanceLevel } = performanceMonitor;
if (performanceLevel === 'poor' && avgFps < 20) {
// Automatically reduce complexity or fps
console.warn('Poor performance detected. Consider reducing animation complexity.');
}
}
}
};

Expand All @@ -282,7 +296,7 @@ const AnimatedBackground = React.memo(({
cancelAnimationFrame(animationRef.current);
}
};
}, [setupCanvas, fps, animationControls]); // Removed changing dependencies
}, [setupCanvas, fps, animationControls, adaptivePerformance]); // Added adaptivePerformance as dependency

// Resize handling effect - separate from animation
useEffect(() => {
Expand Down
23 changes: 19 additions & 4 deletions src/utils/interactionUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export const createInteractionHandler = (canvas, config = {}) => {
effect = 'attract',
strength = 0.5,
radius = 100,
continuous = false
continuous = false,
multiTouch = false
} = config;

let isInteracting = false;
Expand Down Expand Up @@ -151,7 +152,9 @@ export const createInteractionHandler = (canvas, config = {}) => {

const handleTouchStart = (event) => {
event.preventDefault();
Array.from(event.touches).forEach((touch, index) => {
const touches = multiTouch ? Array.from(event.touches) : [event.touches[0]];
touches.forEach((touch) => {
if (!touch) return;
const point = {
x: (touch.clientX - canvas.getBoundingClientRect().left) * (canvas.width / canvas.getBoundingClientRect().width),
y: (touch.clientY - canvas.getBoundingClientRect().top) * (canvas.height / canvas.getBoundingClientRect().height),
Expand All @@ -165,7 +168,9 @@ export const createInteractionHandler = (canvas, config = {}) => {

const handleTouchMove = (event) => {
event.preventDefault();
Array.from(event.touches).forEach((touch) => {
const touches = multiTouch ? Array.from(event.touches) : [event.touches[0]];
touches.forEach((touch) => {
if (!touch) return;
const point = {
x: (touch.clientX - canvas.getBoundingClientRect().left) * (canvas.width / canvas.getBoundingClientRect().width),
y: (touch.clientY - canvas.getBoundingClientRect().top) * (canvas.height / canvas.getBoundingClientRect().height),
Expand All @@ -179,9 +184,19 @@ export const createInteractionHandler = (canvas, config = {}) => {

const handleTouchEnd = (event) => {
event.preventDefault();
Array.from(event.changedTouches).forEach((touch) => {
const changedTouches = Array.from(event.changedTouches);
changedTouches.forEach((touch) => {
touchPoints.delete(touch.identifier);
});

// In single-touch mode, if the primary touch is lifted but other fingers
// are still on the screen, we need to ensure the interaction points are cleared
// to prevent the effect from getting stuck, but only if there are no actual touches left
// from the event.touches.
if (!multiTouch && event.touches.length === 0) {
touchPoints.clear();
}

interactionPoints = Array.from(touchPoints.values());
};

Expand Down
Loading