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;
相关推荐
c2385611 分钟前
# Mini OJ — 项目详解文档
开发语言·数据库·c++
2401_8949155324 分钟前
Geo 优化源码部署常见报错排查:连接失败、地图加载、地域词失效解决方案
服务器·开发语言·python·安全·php
c2385625 分钟前
C/C++每日一练20
c语言·开发语言·c++
mONESY44 分钟前
用 useRef 管理 Worker 线程,让你的页面不再卡顿
javascript
小月土星1 小时前
useCallback 深度解析:React 性能优化的核心利器
react.js·前端框架
可爱系程序猿1 小时前
msvcp140_codecvt_ids.dll 缺失的排查记录:C++ 程序启动异常时如何核对运行库版本
开发语言·c++·程序人生·电脑
weixin_440784111 小时前
【IntentSeivice实现原理】
android·java·开发语言·intentservice
岁岁养乐多2 小时前
Java 序列化相关问题
java·开发语言
CAE二次开发工程师2 小时前
【C语言入门到精通】-基础篇
c语言·开发语言·python
AAIshangyanxiu2 小时前
涡度通量碳观测数据 MATLAB 全流程解析与敏感性定量分析、观测数据质控、插补及 footprint 建模技术
开发语言·matlab·涡度通量·生态碳汇·生态碳汇模拟