react18.x+播放文本内容

需要调接口将文字传递给后端将文字转换成音频文件,然后播放,同时每次播放不同文本时,当前播放的文本需要暂停,切换到播放新点击的文本

可以设置缓存播放过的音频,也可以不设置缓存:

设置缓存的代码如下:

javascript 复制代码
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">
            <Button
              shape="round"
              size="small"
              icon={<SoundOutlined style={{ fontSize: "14px" }} />}
              onClick={() => playMp3Audio(item.text)}
            />
          </Tooltip>
        </div>
      ))}
    </div>
  );
};

export default Home;

不缓存,每次点击都发送请求

javascript 复制代码
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 });
        // 将返回的数据转化为Blob
        const 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为null
        audio.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">
            <Button
              shape="round"
              size="small"
              icon={<SoundOutlined style={{ fontSize: "14px" }} />}
              onClick={() => playMp3Audio(item.text)}
            />
          </Tooltip>
        </div>
      ))}
    </div>
  );
};

export default Home;
相关推荐
敲敲敲-敲代码3 分钟前
游戏设计:推箱子【easyx图形界面/c语言】
c语言·开发语言·游戏
ROC_bird..12 分钟前
STL - vector的使用和模拟实现
开发语言·c++
Ciito15 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
MavenTalk18 分钟前
Move开发语言在区块链的开发与应用
开发语言·python·rust·区块链·solidity·move
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】生产消费模型 & 阻塞队列
java·开发语言·java-ee
fighting ~1 小时前
react17安装html-react-parser运行报错记录
javascript·react.js·html
2401_840192271 小时前
python基础大杂烩
linux·开发语言·python
老码沉思录1 小时前
React Native 全栈开发实战班 - 列表与滚动视图
javascript·react native·react.js
@东辰1 小时前
【golang-技巧】- 定时任务 - cron
开发语言·golang·cron
abments1 小时前
JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)
javascript·爬虫·python