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;
相关推荐
GIS数据转换器1 小时前
地下空间综合管廊运营管理平台
大数据·开发语言·人工智能·经验分享·perl
Wang's Blog7 小时前
Java 项目实战: 交友项目:登录链路回顾与今日任务地图
java·开发语言·状态模式·交友
砚底藏山河7 小时前
python量化:行情数据回溯
java·开发语言·python·金融·maven
今年下半年7 小时前
AI智能体项目实战记录(02):初始化 Python 项目
开发语言·人工智能·python·fastapi
Williamtym7 小时前
PickGo Lab实践:用浏览器、Python和MCP串起机器人搬运仿真
开发语言·python·机器人
DongQiShanRen8 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
CS创新实验室8 小时前
Python 对象“死“了之后去了哪?——引用计数与分代垃圾回收
开发语言·jvm·python
Java后端的Ai之路8 小时前
14、ReactJS - 路由(Router)
前端·react.js·状态模式
持敬chijing8 小时前
白盒测试与代码审计-js登录逆向实战
javascript·网络·安全·web安全·网络安全·网络攻击模型
quantdash_cc8 小时前
Pandas 处理分钟级股票行情:从原始数据清洗到 K 线聚合
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash