需要調接口將文字傳遞給后端將文字轉換成音頻文件,然后播放,同時每次播放不同文本時,當前播放的文本需要暫停,切換到播放新點擊的文本
可以設置緩存播放過的音頻,也可以不設置緩存:
設置緩存的代碼如下:
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;