-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Animation Very Glitchy in Audio Frequency Visualization when Interrupting 'withTiming' ANDROID ONLY #6856
Comments
@mrmuke hey, thanks for the detailed issue and sorry for the late reply (holidays got us haha). Could you please share some recordings pointing out what exactly feels glitchy? I am currently comparing iOS and Android side by side and the animations are super janky but this is solely because of super often updates - both platforms behave the same |
Also, I played a bit with the parameters and making the |
Okay I played even more with it and found some way to make it smoother without changing parameters of the animation: (sorry for typing your import React, { useEffect } from 'react';
import { Svg, Rect } from 'react-native-svg';
import Animated, { useSharedValue, useAnimatedProps, withTiming, interpolate, SharedValue, useDerivedValue, Easing } from 'react-native-reanimated';
import { View } from 'react-native';
const AnimatedRect = Animated.createAnimatedComponent(Rect);
const SVG_CONTAINER_WIDTH = 300
const SVG_CONTAINER_HEIGHT = 300
const ESTIMATE_BAND_MIN_MAGNITUDE = 0
const ESTIMATE_BAND_MAX_MAGNITUDE = 750 // Arbitrary estimate that seems to work for both platforms
const MIN_BAR_HEIGHT = 75;
const MAX_BAR_HEIGHT = 175;
const BAND_ANIMATION_DURATION = 150
const BAND_WIDTH = 60
const BAND_SEPERATION = 12
function BandRect({magnitudeSv, index}: {magnitudeSv: SharedValue<number>, index: number}) {
const heightSv = useSharedValue(MIN_BAR_HEIGHT)
useDerivedValue(() => {
// react to each mutation of magnitudeSv by calculating new height and modifying heightSv
const interpolatedHeight = interpolate(
magnitudeSv.value,
[ESTIMATE_BAND_MIN_MAGNITUDE, ESTIMATE_BAND_MAX_MAGNITUDE],
[MIN_BAR_HEIGHT, MAX_BAR_HEIGHT],
)
heightSv.value = withTiming(interpolatedHeight, { duration: BAND_ANIMATION_DURATION })
})
const animatedProps = useAnimatedProps(() => ({
y: (SVG_CONTAINER_HEIGHT - heightSv.value) / 2,
height: heightSv.value,
}))
return (
<AnimatedRect
x={index * (BAND_WIDTH + BAND_SEPERATION) + BAND_SEPERATION}
width={BAND_WIDTH}
ry={BAND_WIDTH / 2}
fill={"black"}
animatedProps={animatedProps}
/>
);
}
export default function App() {
// Shared values for magnitudes
const magnitudeSvs = [0, 0, 0, 0].map(() => useSharedValue(ESTIMATE_BAND_MIN_MAGNITUDE));
useEffect(() => {
// Fake listener that generates random frequency bands
const fakeFrequencyDataSubscription = setInterval(() => {
const freqBands = Array.from({ length: 4 }, () =>
Math.random() * (ESTIMATE_BAND_MAX_MAGNITUDE - ESTIMATE_BAND_MIN_MAGNITUDE) + ESTIMATE_BAND_MIN_MAGNITUDE
);
freqBands.forEach((band, index) => {
magnitudeSvs[index].value = band
});
}, 30); // make the updates occur faster than withTiming
return () => {
clearInterval(fakeFrequencyDataSubscription);
};
}, []);
// const
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Svg height={SVG_CONTAINER_HEIGHT} width={SVG_CONTAINER_WIDTH}>
{magnitudeSvs.map((h, index) => {
return (
<BandRect magnitudeSv={magnitudeSvs[index]} index={index} key={index}/>
);
})}
</Svg>
</View>
);
}; So what I did is: you both interpolated the height and assigned shared values on JS thread inside In general, the code in Also a second thing - android emulator usually works worse than iOS simulator - only physical android devices show the true performance 😄 LMK if you need anything else! P.S. I'd still try adding some baseline throttle for the updates, if we got some updates in less than a single frame time there still could be some glitches even with this approach |
Hi! Thank you so much for your detailed testing and responding with so much care, I really appreciate it. I also noticed iOS and Android performed similarly on this code after posting this but forgot to change my post details. However, I also realized that it works better on newer androids such as Google Pixel 8 that I tested on and my iPhone 15, but is very laggy on an older Android A32 5G. Awesome that you found something that works without changing params! I will try out your solution and get back to you on how it performs on the older Android phone. |
Also, I tried throttling before and the problem with it is that it doesn't look nearly as snappy and reactive to your voice even with a slight delay. |
I get it, throttling might limit the usefulness in your use case. Regarding the devices, it is usually true that simulators/emulators will be working worse than mid- and high-end devices and better than some older ones. Unfortunately that's an arms race that will always be a case. |
Description
I am trying to create an animation for frequency band visualization using withTiming. The useEffect hook listens for frequency data changes and applies interpolated height values to animated bands, but the updates, which use withTiming with a specified animation duration, do not appear smooth whenever the frequency data is coming in faster than the animation duration. However, this works totally fine on iOS and is able to replace and continue each animation smoothly, but totally breaks down when on android.
According to the react-native-reanimated docs:
Expected Behavior:
Observed Behavior:
Steps to reproduce
Snack or a link to a repository
https://github.com/mrmuke/animating_rectangles
Reanimated version
3.16.1
React Native version
0.76.5
Platforms
Android
JavaScript runtime
None
Workflow
None
Architecture
None
Build type
None
Device
None
Device model
No response
Acknowledgements
Yes
The text was updated successfully, but these errors were encountered: