Search code examples
javascripttypescriptreact-nativereact-native-reanimated-v2

How to visualize sound waves like Google Meet does while a person talking in react native


I am trying to accomplish very similar animation in the below example.

Google Meet Voice Visualization Example

My approach was like this: Get the voice volume data, append it to a view's borderWidth using a sharedValue of react-native-reanimated. However, since borderWidth append itself to inside of the View, the visualization is look like in the following example:

enter image description here

below you can find my code,

initialization of react-native-reanimated related states.

  const voiceVolumeMeter = useSharedValue(0);
  const voiceVolumeMeterStyles = useAnimatedStyle(() => {
    return {
      borderWidth: voiceVolumeMeter.value,
    };
  });

appending volume data to the voiceVolumeMeter parameter:

  voiceVolumeMeter.value = withSpring(55 + e.currentMetering!, {
    stiffness: 90,
    velocity: 12,
    mass: 0.5,
  });

applying animatedStyle to View

<Animated.View style={[styles.volumeMeterContainer, voiceVolumeMeterStyles]}>
  <View style={styles.recorderCircle}>
    <Text style={styles.audioRecorderTimer}>{recorderState.duration}</Text>
  </View>
</Animated.View>

styles that used above code example:

  recorderCircle: {
    width: 280,
    height: 280,
    borderRadius: 150,
    borderColor: Colors.DUSTY_ORANGE,
    justifyContent: 'center',
    alignItems: 'center',
  },

  volumeMeterContainer: {
    width: 290,
    height: 290,
    borderRadius: 140.5,
    borderColor: Colors.WINDOWS_BLUE,
    justifyContent: 'center',
    padding: 20,
  },

  audioRecorderTimer: {
    fontFamily: 'Quicksand',
    fontStyle: 'normal',
    fontWeight: '700',
    fontSize: 45,
    lineHeight: 56,
    textAlign: 'center',
    letterSpacing: -0.746023,
    color: Colors.DUSTY_ORANGE,
  },

This approach basically produced exactly reversed version of Google's visualization. I have tried with css's outline props but unfortunately, outline props are not supported on react native. so i need another approach but i could not come up with a brilliant idea here.

Any help will be appreciated,

Best regards.


Solution

  • Hey thanks to your question i did this , to replicate meet chat,

    This is the live example here, feel free for doubts, expo-snack-link

    Your answer herrenter image description here

    import React, { PureComponent ,useEffect,useCallback , useState } from 'react';
    import { StyleSheet, View, TouchableWithoutFeedback, Animated, Easing, Platform ,Text} from 'react-native';
    
    
    
    
    const AppNew = (props={}) => {
    
    const currentVolume = props?.currentVolume ?? 0
    
    const maxVolume = 50;
    
    const animationRef = React.useRef(new Animated.Value(0)).current
    
    const startAnimations = useCallback(() => {
      Animated.timing(animationRef,{
        toValue:(currentVolume/maxVolume),
        useNativeDriver:true,
        duration:500
      }).start()
    },[animationRef,currentVolume])
    
    useEffect(() => {
    startAnimations()
    },[startAnimations])
    
    const polAnim = animationRef.interpolate({
      inputRange:[0,1],
      outputRange:[1,2],
      extrapolate:'clamp'
    })
    
      return(
        <Animated.View style={[styles.ripler,{
    
          position:'absolute',
          height:60,
          width:60,
          borderRadius:120,
    
          transform:[{
            scale:polAnim
          }]
        }]} >
        </Animated.View>
      )
    }
      const data = [1,6,39,40,50,22,7,15,12,1,6,39,40,50,22,7,15,18,1,6,39,40,50,22,7,15,12,1,6,39,40,50,22,7,15,12,1,6,39,40,50,22,7,15,12,1,6,39,40,50,22,7,15,12]
    
    const App  = () => {
    
        const [currentTime, setTime ] = useState(0);
        const [currIndex,setIndex] = useState(0)
    
        useEffect(() => {
         const interval = setInterval(() => {
            const newIndex = currIndex +1;
            setTime(data[currIndex]);
            setIndex(newIndex);
          },500)
          return () => clearInterval(interval)
        },[currIndex]) 
    
            return (
                <View style={styles.pageContainer}>   
                  <View style={{height:100,width:100}} >
                  <AppNew currentVolume={currentTime} />
                  <View style={{height:60,width:60,borderRadius:60,
                  alignItems:'center',
                  justifyContent:'center',
                  backgroundColor:'rgba(125,244,102,0.9)',
                  zIndex:3
                  }} >
                  <Text>Messi</Text>
                  </View>
                  </View>
                </View>
            );
    
    }
    
    
    
    
    const styles = StyleSheet.create({
        pageContainer: {
            flex: 1,
            alignItems:'center',
            justifyContent:'center'
        },
        ripler:{
     backgroundColor:'rgba(125,244,102,0.3)',
     zIndex:2
        },
        contentContainer: {
            flex: 1,
            justifyContent: 'center',
            alignItems: 'center',
        },
        iconContainer: {
            margin: 16, 
            alignItems: 'center',
            justifyContent: 'center',
        },
    });
    
    
    export default App;