需要调接口将文字传递给后端将文字转换成音频文件,然后播放,同时每次播放不同文本时,当前播放的文本需要暂停,切换到播放新点击的文本
可以设置缓存播放过的音频,也可以不设置缓存:
设置缓存的代码如下:
import React, { useState, useCallback, useRef } from "react";
import { Button, Tooltip } from "antd";
import { SoundOutlined } from "@ant-design/icons";
import { getMp3AudioByText } from "./api/audio";const Home = () => {const [texts, setTexts] = useState([{ id: 1, text: "useEffect:允许你将组件与外部系统同步" },{ id: 2, text: "useCallback:是一个允许在多次渲染中缓存函数的React Hook" },{ id: 3, text: "useRef:能帮助引用一个不需要渲染的值" },{ id: 4, text: "useState:向组件添加一个状态变量" },{ id: 5, text: "useMemo:在每次重新渲染的时候能够缓存计算的结果" },{ id: 6, text: "useId:是一个可以生成传递给无障碍属性的唯一 ID" },]);const [currentAudio, setCurrentAudio] = useState<HTMLAudioElement | null>(null);const audioCache = useRef<{ [key: string]: string }>({});const cacheOrder = useRef<string[]>([]);const MAX_CACHE_SIZE = 5;const playMp3Audio = useCallback(async (content: string) => {try {if (currentAudio) {currentAudio.pause(); // 如果当前有正在播放的音频,暂停音频currentAudio.currentTime = 0; // 重置音频的当前播放时间setCurrentAudio(null); // 清除当前音频元素的状态}if (audioCache.current[content]) {// 如果缓存中有对应的音频URL,直接播放const audio = new Audio(audioCache.current[content]);audio.play().catch((playError) => {console.error("无法播放音频", playError);return;});setCurrentAudio(audio); // 更新当前音频元素状态audio.addEventListener("ended", () => setCurrentAudio(null));} else {// 否则发送请求获取音频const response: any = await getMp3AudioByText({ text: content });const blob = new Blob([response], { type: "audio/mp3" });const url = URL.createObjectURL(blob);// 将音频URL存入缓存if (cacheOrder.current.length >= MAX_CACHE_SIZE) {const oldestKey = cacheOrder.current.shift()!;URL.revokeObjectURL(audioCache.current[oldestKey]);delete audioCache.current[oldestKey];}audioCache.current[content] = url;cacheOrder.current.push(content);const audio = new Audio(url);audio.play().catch((playError) => {console.error("无法播放音频", playError);return;});setCurrentAudio(audio); // 更新当前音频元素状态audio.addEventListener("ended", () => setCurrentAudio(null));}} catch (error) {console.error(error);}},[currentAudio]);return (<div>{texts.map((item) => (<div key={item.id}>{item.text}<Tooltip title="播放文本" color="pink"><Buttonshape="round"size="small"icon={<SoundOutlined style={{ fontSize: "14px" }} />}onClick={() => playMp3Audio(item.text)}/></Tooltip></div>))}</div>);
};export default Home;
不缓存,每次点击都发送请求
import React, { useState, useCallback } from "react";
import classNames from "classnames";
import { Button, Tooltip } from "antd";
import { SoundOutlined } from "@ant-design/icons";
import { getMp3AudioByText } from "./api/audio";const Home = () => {const [texts, setTexts] = useState([{ id: 1, text: "useEffect:允许你将组件与外部系统同步" },{ id: 2, text: "useCallback:是一个允许在多次渲染中缓存函数的React Hook" },{ id: 3, text: "useRef:能帮助引用一个不需要渲染的值" },{ id: 4, text: "useState:向组件添加一个状态变量" },{ id: 5, text: "useMemo:在每次重新渲染的时候能够缓存计算的结果" },{ id: 6, text: "useId:是一个可以生成传递给无障碍属性的唯一 ID" },]);const [currentAudio, setCurrentAudio] = useState<HTMLAudioElement | null>(null);const playMp3Audio = useCallback(async (content: string) => {try {if (currentAudio) {currentAudio.pause();// 如果当前有正在播放的音频,暂停音频currentAudio.currentTime = 0;// 重置音频的当前播放时间setCurrentAudio(null);// 清除当前音频元素的状态}const response: any = await getMp3AudioByText({ text: content });// 将返回的数据转化为Blobconst blob = new Blob([response], { type: "audio/mp3" });const url = URL.createObjectURL(blob);// 创建一个新的audio元素来播放mp3文件const audio = new Audio(url);audio.play().catch((playError) => {console.error("无法播放音频", playError);return;});setCurrentAudio(audio); // 更新当前音频元素状态// 添加'ended'事件监听器,当音频播放结束时设置currentAudio为nullaudio.addEventListener("ended", () => setCurrentAudio(null));} catch (error) {console.error(error);}},[currentAudio]);return (<div>{texts.map((item, index) => (<div key={index}>{item.text}<Tooltip title="播放文本" color="pink"><Buttonshape="round"size="small"icon={<SoundOutlined style={{ fontSize: "14px" }} />}onClick={() => playMp3Audio(item.text)}/></Tooltip></div>))}</div>);
};export default Home;