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;
相关推荐
JQLvopkk6 分钟前
C# 轻量级工业温湿度监控系统(含数据库与源码)
开发语言·数据库·c#
要加油哦~18 分钟前
AI | 实践教程 - ScreenCoder | 多agents前端代码生成
前端·javascript·人工智能
玄同76520 分钟前
从 0 到 1:用 Python 开发 MCP 工具,让 AI 智能体拥有 “超能力”
开发语言·人工智能·python·agent·ai编程·mcp·trae
czy878747521 分钟前
深入了解 C++ 中的 `std::bind` 函数
开发语言·c++
消失的旧时光-194326 分钟前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
yq19820430115627 分钟前
静思书屋:基于Java Web技术栈构建高性能图书信息平台实践
java·开发语言·前端
一个public的class28 分钟前
你在浏览器输入一个网址,到底发生了什么?
java·开发语言·javascript
Jinkxs30 分钟前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin
&有梦想的咸鱼&30 分钟前
Kotlin委托机制的底层实现深度解析(74)
android·开发语言·kotlin
青茶36037 分钟前
php怎么实现订单接口状态轮询请求
前端·javascript·php