在React js中从子组件播放音频不起作用

问题描述 投票:0回答:1

我开始学习react.js已经有一段时间了,但现在我在播放和暂停子组件LessonCard.jsx的音频时遇到问题。当我单击 LessonCard.jsx 中的播放按钮时,它正在播放音频,这是另一个组件 AudioPlayer.jsx 但问题是,当我暂停 LessonCard.jsx 中的音频时,按钮的图标会发生变化,但音频是没有暂停。

Home.jsx

import React, { useState, useEffect } from "react";
import sanityClient from "../client";
import CategoryCard from "./CategoryCard";
import LessonCard from "./LessonCard";
import AudioPlayer from "./AudioPlayer";

const Home = () => {
  const [lessons, setLessons] = useState(null);
  const [loading, setLoading] = useState(true);
  const [audioUri, setAudioUri] = useState(null);
  const [isAudioPlaying, setIsAudioPlaying] = useState(false);

  const handleCardClick = (audioUri) => {
    setAudioUri(audioUri);
    setIsAudioPlaying((prevIsAudioPlaying) => !prevIsAudioPlaying);
  };
  // const query = '*[_type == "lessons"]';

  useEffect(() => {
    const fetchData = async () => {
      try {
        const result = await sanityClient.fetch(`*[_type == 'lessons']{
          _id,
          lessonTitle,
          category,
          uploadDate,
          body,
          'audio': audio.asset->url
        }`);

        setLessons(result);
        setLoading(false);
        // console.log(result);
      } catch (error) {
        console.error("Error fetching data from Sanity:", error);
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  return (
    <main className="container 2xl:max-w-[1280px] mx-auto min-h-[100vh] px-4 py-16">
      <div>
        <h2 className="mt-[2rem] text-3xl ff-bold font-semibold text-[color:var(--deep-green)]">
          Categories
        </h2>
        <CategoryCard />

        <h2 className="mt-[3rem] text-3xl ff-bold font-semibold text-[color:var(--deep-green)]">
          All available lessons
        </h2>

        <div
          className={`min-h-[50vh] ${
            loading ? "flex items-center justify-center" : ""
          }`}
        >
          {loading ? (
            <div className="text-center text-xl">Loading...</div>
          ) : (
            <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 sm:gap-3 mt-6">
              {lessons?.map((lesson, idx) => {
                const { lessonTitle, category, uploadDate, audio } = lesson;
                return (
                  <LessonCard
                    key={idx}
                    lessonTitle={lessonTitle}
                    category={category}
                    publishDate={uploadDate}
                    audioUri={audio}
                    // isAudioPlaying={isAudioPlaying}
                    isAudioPlaying={audioUri === lesson.audio && isAudioPlaying}
                    setIsAudioPlaying={setIsAudioPlaying}
                    setAudioUri={setAudioUri}
                  />
                );
              })}
            </div>
          )}
        </div>
      </div>

      <AudioPlayer
        audioUri={audioUri}
        isAudioPlaying={isAudioPlaying}
        setIsAudioPlaying={setIsAudioPlaying}
      />
    </main>
  );
};

export default Home;

LessonCard.jsx

import React, { useRef } from "react";

const LessonCard = ({
  lessonTitle,
  category,
  publishDate,
  audioUri,
  isAudioPlaying,
  setIsAudioPlaying,
  setAudioUri,
}) => {
  const lessonCardRef = useRef(null);

  const handleCardPlay = () => {
    setAudioUri(audioUri);
    setIsAudioPlaying((prevIsAudioPlaying) => !prevIsAudioPlaying);
  };

  return (
    <div
      ref={lessonCardRef}
      onClick={handleCardPlay}
      className="lesson-card w-full bg-white border border-slate-200 hover:border-[color:var(--deep-green)] duration-150 h-[70px] px-2 rounded-[7px] cursor-pointer group flex justify-between active:border-[color:var(--deep-green)]"
    >
      <div className="w-full h-full flex items-center">
        <div className="h-full flex items-center justify-center">
          <button className="[color:var(--deep-green)] bg-slate-100 w-[40px] h-[40px] rounded-full group-hover:bg-slate-500 group-hover:text-white duration-150 flex items-center justify-center font-semibold text-lg mr-3">
            <i className={isAudioPlaying ? "fa fa-pause" : "fa fa-play"}></i>
          </button>
        </div>

        <div className="flex-1">
          <p className="text-lg truncate text-ellipsis line-clamp-1 break-words whitespace-normal">
            {lessonTitle}
          </p>

          <div className="flex items-center justify-between">
            <p className="text-sm capitalize text-[color:rgba(0,0,0,0.7)]">
              {category}
            </p>
            <p className="[font-size:0.75rem] text-[color:rgba(0,0,0,0.7)]">
              {publishDate}
            </p>
          </div>
        </div>

        <div className="hidden w-[40px] h-[40px] rounded-full bg-slate-200  items-center justify-center">
          <button>
            <i className="fa fa-download"></i>
          </button>
        </div>
      </div>
    </div>
  );
};

export default LessonCard;

音频播放器.jsx

import React, { useEffect, useRef, useState } from "react";
import WaveSurfer from "wavesurfer.js";

const AudioPlayer = ({ audioUri, isAudioPlaying, setIsAudioPlaying }) => {
  const playerRef = useRef(null);
  // const [isPlaying, setIsPlaying] = useState(false);
  const wavesurferRef = useRef(null);

  const handlePlay = () => {
    if (wavesurferRef.current) {
      if (isAudioPlaying) {
        wavesurferRef.current.pause();
      } else {
        wavesurferRef.current.play();
      }
      setIsAudioPlaying(!isAudioPlaying);
    }
  };

  const downloadAudio = () => {
    const link = document.createElement("a");
    link.href = audioUri;
    link.download = "your-audio.mp3";
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    setTimeout(() => {
      link.remove();
    }, 50);
  };

  useEffect(() => {
    wavesurferRef.current = WaveSurfer.create({
      container: playerRef.current,
      waveColor: "#BFCEBF",
      progressColor: "#1e8a69",
      cursorColor: "transparent",
      barWidth: 3,
      barHeight: 2,
      barGap: 2,
      barRadius: 5,
      height: 60,
      normalize: true,
      responsive: true,
      dragToSeek: true,
      autoScroll: true,
    });

    // Load the audio file when the URI changes
    if (wavesurferRef.current) {
      if (audioUri) {
        wavesurferRef.current.load(audioUri);

        wavesurferRef.current.on("ready", () => {
          if (isAudioPlaying) {
            wavesurferRef.current.play();
          } else {
            wavesurferRef.current.pause();
          }
        });
      }
    }

    return () => {
      wavesurferRef.current.destroy();
    };
  }, [audioUri]);

  return (
    <div className="container mx-auto 2xl:max-w-[1280px]">
      <div
        className={`${
          audioUri
            ? "fixed bottom-0 left-0 z-20 bg-slate-100 w-full px-4 py-2 flex items-center justify-center  translate-y-[0%] [transition:300ms]"
            : "translate-y-[100%]"
        }`}
      >
        <div className="max-w-[37rem] mx-auto w-full flex items-center">
          <div
            onClick={handlePlay}
            className={`${
              audioUri ? "flex items-center justify-center mr-4" : "hidden"
            }`}
          >
            <button className="w-[35px] h-[35px] sm:w-[40px] sm:h-[40px] rounded-full bg-[var(--deep-green)]">
              <i
                className={`fa ${
                  isAudioPlaying ? "fa-pause" : "fa-play"
                } text-white duration-150`}
              ></i>
            </button>
          </div>
          <div ref={playerRef} className="player w-full z-40 gap-2 sm:gap-4">
            <div
              onClick={downloadAudio}
              className={`${
                audioUri ? "flex items-center justify-center" : "hidden"
              }`}
            >
              <button className="w-[35px] h-[35px] sm:w-[40px] sm:h-[40px] rounded-full bg-slate-200 hover:bg-[var(--deep-green)]  group duration-150">
                <i className="fa fa-download text-[var(--deep-green)] group-hover:text-white duration-150"></i>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default AudioPlayer;

我尝试播放 LessonCard.jsx 组件中的音频,但它不起作用。

reactjs audio react-hooks
1个回答
0
投票

在您的

AudioPlayer
组件中,每当
isAudioPlaying
状态在组件外部发生更改时(即当用户按下课程卡时),就没有暂停/播放
wavesurfer
的指令。您可以在
useEffect
组件中添加以下
AudioPlayer
钩子:

useEffect(() => {
  if (isAudioPlaying) {
    wavesurferRef.current?.play();
  } else {
    wavesurferRef.current?.pause();
  }
}, [isAudioPlaying]);

这意味着每当状态

isAudioPlaying
更新时,
wavesurfer
就会相应地播放或暂停。此外,您可以简化您的
handlePlay
函数:

const handlePlay = () => {
  setIsAudioPlaying(isAudioPlaying => !isAudioPlaying);
};
© www.soinside.com 2019 - 2024. All rights reserved.