背景
最近做一个学校作业,用 React Native 写一个游戏。游戏中涉及一个物体下落的动效,是用 React Native 的 Animated API 实现的。
现在希望在物体落地的时候发出“嗒”的一个音效,没想到还挺麻烦。
首先需要一个库来实现音频的播放。网上搜了一下,大部分人的解决方案都是用 react-native-sound 这个库。我照着试了一下,安装很麻烦不说,即便我只是像下面这样引入这个库:
import Sound from 'react-native-sound';
也会一直报这个错误:
TypeError: null is not an object (evaluating 'RNSound.IsAndroid')
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:104:6 in reportException
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:172:19 in handleException
at node_modules/react-native/Libraries/Core/setUpErrorHandling.js:24:6 in handleError
at node_modules/@react-native/polyfills/error-guard.js:49:36 in ErrorUtils.reportFatalError
at node_modules/metro-runtime/src/polyfills/require.js:204:6 in guardedLoadModule
at http://192.168.1.5:19000/node_modules/expo/AppEntry.bundle?platform=android&dev=true&hot=false&minify=false:488211:3 in global code
后来突然想到,我这个项目是基于 Expo 的,如果 Expo 提供了音频解决方案就好了。搜了一下还真有!
后来又捣鼓了一阵,终于实现了。
实现
代码
示例代码如下:
import { useRef, useEffect } from 'react';
import { Animated, Dimensions, Easing } from 'react-native';
import { Audio } from 'expo-av'; // 引入该库
const WINDOW_WIDTH = Dimensions.get('window').width;
const WINDOW_HEIGHT = Dimensions.get('window').height;
const BOX_SIZE = WINDOW_WIDTH / 5;
const BOX_INIT_X = WINDOW_WIDTH/2 - BOX_SIZE/2;
const BOX_INIT_Y = WINDOW_WIDTH/2;
const BOX_FINAL_Y = WINDOW_HEIGHT - BOX_SIZE
let crashSound; // 音效
export default function App() {
const positionY = useRef(new Animated.Value(BOX_INIT_Y)).current
// 加载音效
useEffect(async () => {
const { sound } = await Audio.Sound.createAsync(
require('./crash_sound.m4a')
);
crashSound = sound;
}, [])
// 设置动画
useEffect(() => {
Animated.timing(
positionY,
{
toValue: BOX_FINAL_Y,
duration: 1000,
useNativeDriver: false,
easing: Easing.ease
}
).start(() => {
// 这个是动效的回调函数,会在动效结束之后被调用,然后播放音效
// 注意用 replayAsync 而不是 playAsync
crashSound.replayAsync();
});
}, [])
const boxStyle = {
position: 'absolute',
top: positionY,
left: BOX_INIT_X,
width: BOX_SIZE,
height: BOX_SIZE,
backgroundColor: 'red'
}
return <Animated.View style={boxStyle} />
}
注意,使用 replayAsync
而不是 playAsync
,是因为 playAsync 只能播一次,第二次调用就没声音了;而 replayAsync 每次调用都有声音。
效果
示例视频