嗨:)我正在尝试制作一个应用程序,其中有几个环境声音音频。我想添加的一项功能是,当我单击复选框时,所有正在播放的音频都将停止,而当我取消选中该复选框时,只有在选中之前正在播放的音频才会恢复。
我正在使用 React 和 Redux。这就是我拥有的。
import "./styles.css";
import { useRef, useReducer } from "react";
const audioActionType = {
TOGGLE_PLAY: "TOGGLE_PLAY",
SET_VOLUME: "SET_VOLUME",
TOGGLE_ALL_PAUSED: "TOGGLE_ALL_PAUSED",
};
const initialAudioState = {
audios: [
{
playing: false,
id: 1,
volume: 25,
title: "Coffe making",
src: "..//audio/espresso-machine.mp3",
},
{
playing: false,
id: 2,
volume: 25,
title: "Coffe machine",
src: "..//audio/espresso-machine.mp3",
},
],
allPaused: false,
};
function AudioReducer(stateAudios, actionAudios) {
switch (actionAudios.type) {
case audioActionType.TOGGLE_PLAY:
return {
...stateAudios,
audios: stateAudios.audios.map((audio) =>
audio.id === actionAudios.id
? { ...audio, playing: !audio.playing }
: audio
),
};
case audioActionType.SET_VOLUME:
return {
...stateAudios,
audios: stateAudios.audios.map((audio) =>
audio.id === actionAudios.id
? { ...audio, volume: actionAudios.volume }
: audio
),
};
case audioActionType.TOGGLE_ALL_PAUSED:
const anyAudioPlaying = { ...stateAudios.audios };
console.log(anyAudioPlaying);
if (stateAudios.allPaused) {
return {
...stateAudios,
audios: stateAudios.audios.map((audio) => ({
...audio,
playing: false,
})),
allPaused: false,
};
default:
return stateAudios;
}
}
export default function App() {
const [state, dispatch] = useReducer(AudioReducer, initialAudioState);
const audioRefs = useRef(
//creaamos una referencia para los audios para poder pausarlos, cambiar el volumen, etc
state.audios.reduce((acc, audio) => {
//con reduce recorremos el array de audios y creamos un objeto que almacena todos los aduios con el acc (contador) como el id del audio y su valor como el src del audio. quedaria algo así: {1: src1.mp3, 2:src2.mp3}
acc[audio.id] = new Audio(audio.src);
return acc;
}, {})
).current; //el current lo ponemos aquí para no tener que usarlo cada vez que queramos acceder al valor del Ref
const togglePlay = (id) => {
dispatch({ type: "TOGGLE_PLAY", id });
const audio = audioRefs[id];
state.audios.find((audio) => audio.id === id).playing
? audio.pause()
: audio.play();
};
const setVolume = (id, volume) => {
dispatch({ type: "SET_VOLUME", id, volume });
audioRefs[id].volume = volume / 100;
};
console.log(state);
return (
<div id="sounds-section" className="sections">
<input
type="checkbox"
id="pause"
name="pause"
checked={state.allPaused}
onChange={() => dispatch({ type: "TOGGLE_ALL_PAUSED" })}
/>
<label htmlFor="pause"> Pause all</label>
<div id="sounds">
{state.audios.map((audio) => (
<div className="sound" key={audio.id}>
<div>
<button
onClick={() => togglePlay(audio.id)} //es importante poner el () porque sinó estamos llamando a la función cuando se crea el objeto y no cuando clicamos en el botón
dangerouslySetInnerHTML={{
__html: audio.playing ? "⏸" : "▶",
}}
/>
<h4>{audio.title}</h4>
</div>
{audio.playing && (
<input
className="volume"
type="range"
min="0"
max="100"
step="1"
value={audio.volume}
onChange={(e) => {
setVolume(audio.id, e.target.value);
}}
/>
)}
</div>
))}
</div>
</div>
);
}
我在 allPaused 将所有播放属性转换为 false 之前无法查看之前的状态。
也许在
audioActionType.TOGGLE_ALL_PAUSED
减速器情况下,您只切换 allPaused
状态并像您一样处理 UI 中的播放,但进行修改以考虑 allPaused
状态。当 allPaused
状态再次切换时,您仍然会拥有正在播放的内容的任何现有状态,因为它从未更新过。
音频减速器
function AudioReducer(stateAudios, actionAudios) {
switch (actionAudios.type) {
case audioActionType.TOGGLE_PLAY:
return {
...stateAudios,
audios: stateAudios.audios.map((audio) =>
audio.id === actionAudios.id
? { ...audio, playing: !audio.playing }
: audio
),
};
case audioActionType.SET_VOLUME:
return {
...stateAudios,
audios: stateAudios.audios.map((audio) =>
audio.id === actionAudios.id
? { ...audio, volume: actionAudios.volume }
: audio
),
};
case audioActionType.TOGGLE_ALL_PAUSED:
return {
...stateAudios,
allPaused: !stateAudios.allPaused,
};
default:
return stateAudios;
}
}
应用程序
export default function App() {
const [state, dispatch] = useReducer(AudioReducer, initialAudioState);
const audioRefs = useRef(
state.audios.reduce((acc, audio) => {
acc[audio.id] = new Audio(audio.src);
return acc;
}, {})
).current;
const togglePlay = (id) => {
dispatch({ type: "TOGGLE_PLAY", id });
const audio = audioRefs[id];
state.audios.find((audio) => audio.id === id).playing
? audio.pause()
: audio.play();
};
const setVolume = (id, volume) => {
dispatch({ type: "SET_VOLUME", id, volume });
audioRefs[id].volume = volume / 100;
};
const toggleAllPaused = () => {
dispatch({ type: "TOGGLE_ALL_PAUSED" });
state.audios.forEach(audio => {
// If all audios are currently paused
if (state.allPaused) {
// And the current audio was previously playing, play audio
if (audio.playing) {
audio.play();
}
// Else just pause all the adios
} else {
audio.pause();
}
});
};
return (
<div id="sounds-section" className="sections">
<input
type="checkbox"
id="pause"
name="pause"
checked={state.allPaused}
onChange={toggleAllPaused}
/>
<label htmlFor="pause"> Pause all</label>
<div id="sounds">
{state.audios.map((audio) => (
<div className="sound" key={audio.id}>
<div>
<button
onClick={() => togglePlay(audio.id)}
dangerouslySetInnerHTML={{
__html: audio.playing ? "⏸" : "▶",
}}
/>
<h4>{audio.title}</h4>
</div>
{audio.playing && (
<input
className="volume"
type="range"
min="0"
max="100"
step="1"
value={audio.volume}
onChange={(e) => {
setVolume(audio.id, e.target.value);
}}
/>
)}
</div>
))}
</div>
</div>
);
}