音频无法在 localhost 上播放,但在 codepen

时间:2021-01-15 14:34:22

标签: reactjs

我在 freeCodeCamp 上写了一个 25 + 5 时钟作为项目。它在可待因上运行良好,并按应有的方式播放音频。但是,当我尝试在 create-react-app 上本地播放它时,我使音频在计时器归零时不播放。我决定使用 onClick 测试音频,然后它就可以工作了。我认为它必须与在 useEffect 期间调用它的事实有关,但我不确定。任何帮助表示赞赏!

计时器归零后出现的错误是这个。

× 类型错误:试图分配给只读属性。 播放声音 src/index.js:115

  112 | function playSound() {
  113 |   const audio = document.getElementById("beep");
  114 |   audio.removeAttribute("readonly");
> 115 |   audio.duration = 1; // Need to pass first audio test
      | ^  116 |   audio.play();
  117 | }
  118 | return (

这里是代码给大家看看。

import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./index.css";

function Timer() {
  const [breakLength, setBreakLength] = useState(5);
  const [sessionLength, setSessionLength] = useState(25);
  const [timer, setTimer] = useState(1500); // 1500 is 25 minutes, but in seconds
  const [timerType, setTimerType] = useState("session");
  const [timerState, setTimerState] = useState("stopped");
  const [timeoutId, setTimeoutId] = useState("");

  React.useEffect(() => {
    // If timer is > 0 and the timerState is clicked to running
    if (timer > 0 && timerState === "running") {
      
      setTimeoutId(setTimeout(() => setTimer(timer - 1), 1000)); // Must use setTimeout as it only triggers expression once. setInterval will continuously call expression until told otherwise.
    }
    // If session timer ends.
    else if (timer === 0 && timerType === "session") {
      setTimerType("break"); // Change timer type back to break
      setTimer(breakLength * 60); // Multiply by 60 because we need to convert minutes into seconds
      playSound();
    }
    // If break timer ends
    else if (timer === 0 && timerType === "break") {
      setTimerType("session"); // Change timer type break
      setTimer(sessionLength * 60); // Multiply by 60 because we need to convert minutes into seconds
      playSound();
    }
    clearTimeout(timeoutId);
   
  }, [timer, timerState, timerType, breakLength, sessionLength]);

  function resetClick() {
    // simply reset all states and audio must be paused then set back to beginning with currentTime = 0
    setTimerState("stopped");
    setBreakLength(5);
    setSessionLength(25);
    setTimerType("session");
    setTimer(1500);
    const audio = document.getElementById("beep");
    audio.pause();
    audio.currentTime = 0;
  }

  function decrementBreakClick() {
    // Doesn't let break length go below 1 and timer must be stopped
    if (breakLength > 1 && timerState === "stopped") {
      setBreakLength(breakLength - 1);
    }
  }

  function incrementBreakClick() {
    // Doesn't let break length go above 60 and timer must be stopped
    if (breakLength < 60 && timerState === "stopped") {
      setBreakLength(breakLength + 1);
    }
  }

  function decrementSessionClick() {
    // Doesn't let session length go below 1 and timer must be stopped
    if (sessionLength > 1 && timerState === "stopped") {
      setSessionLength(sessionLength - 1);
      setTimer(timer - 60); // Take away 60 as timer is set in seconds.
    }
  }

  function incrementSessionClick() {
    // Doesn't let session length go below 1 and timer must be stopped
    if (sessionLength < 60 && timerState === "stopped") {
      setSessionLength(sessionLength + 1);
      setTimer(timer + 60); // Add 60 as timer is set in seconds.
    }
  }

  function startStopClick() {
    // if state is stopped then change it to running. Else change running back to stopped
    if (timerState === "stopped") {
      setTimerState("running");
    } else {
      setTimerState("stopped");
    }
  }

  // Convert the timer state that is in seconds to minutes and seconds.
  function timerToClock() {
    let minutes = Math.floor(timer / 60); 
    let seconds = timer - minutes * 60; 
    minutes = minutes < 10 ? "0" + minutes : minutes; 
    seconds = seconds < 10 ? "0" + seconds : seconds; 
    return minutes + ":" + seconds;
  }

  function playSound() {
    const audio = document.getElementById("beep");
    audio.removeAttribute("readonly");
    audio.duration = 1; // Need to pass first audio test
    audio.play();
  }
  return (
    <div>
      <h1 id="header">25 + 5 Clock</h1>
      <div id="machine-container">
        <div id="break-session-containter">
          <BreakLength
            breakLength={breakLength}
            decrement={decrementBreakClick}
            increment={incrementBreakClick}
          />
          <SessionLength
            decrement={decrementSessionClick}
            increment={incrementSessionClick}
            sessionLength={sessionLength}
          />
        </div>
        <div id="timer-container">
          <div id="timer-div">
            <h2 id="timer-label">{timerType}</h2>
            {/*Calling a function so need to add () */}
            <span id="time-left">{timerToClock()}</span>
          </div>
        </div>
        <div id="timer-controls-container">
          <button id="start_stop" onClick={startStopClick}>
            <i className="fa fa-play"></i>
            <i className="fa fa-pause"></i>
          </button>
          <button id="reset" onClick={resetClick}>
            <i className="fa fa-sync"></i>
          </button>

          <audio
            id="beep"
            src="https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav"
          ></audio>
        </div>

        <div id="credit-container">
          Designed and coded by:
          <br />
          <a
            href="https://codepen.io/your-work/"
            target="_blank"
            rel="noreferrer"
          >
            Hunter Lacefield
          </a>
        </div>
      </div>
    </div>
  );
}

function BreakLength(props) {
  return (
    <div id="break-length-container">
      <h3 id="break-label">Break Length</h3>
      <button
        id="break-decrement"
        className="down-button"
        onClick={props.decrement}
      >
        <i className="fa fa-arrow-down"></i>
      </button>

      <span id="break-length" className="break-session-number">
        {props.breakLength}
      </span>

      <button
        id="break-increment"
        className="up-button"
        onClick={props.increment}
      >
        <i className="fa fa-arrow-up"></i>
      </button>
    </div>
  );
}

function SessionLength(props) {
  return (
    <div id="session-length-container">
      <h3 id="session-label">Session Length</h3>
      <button
        id="session-decrement"
        className="down-button"
        onClick={props.decrement}
      >
        <i className="fa fa-arrow-down"></i>
      </button>

      <span id="session-length" className="break-session-number">
        {props.sessionLength}
      </span>

      <button
        id="session-increment"
        className="up-button"
        onClick={props.increment}
      >
        <i className="fa fa-arrow-up"></i>
      </button>
    </div>
  );
}

ReactDOM.render(<Timer />, document.getElementById("root"));

1 个答案:

答案 0 :(得分:1)

我认为问题在于你clearTimeout一开始就启动它。

React.useEffect(() => {
    let timeoutId;
    // If timer is > 0 and the timerState is clicked to running
    if (timer > 0 && timerState === "running") {
      
      timeoutId= setTimeout(() => setTimer(timer - 1), 1000);
     // Must use setTimeout as it only triggers expression once. setInterval will continuously call expression until told otherwise.
    }
    // If session timer ends.
    else if (timer === 0 && timerType === "session") {
      setTimerType("break"); // Change timer type back to break
      setTimer(breakLength * 60); // Multiply by 60 because we need to convert minutes into seconds
      playSound();
    }
    // If break timer ends
    else if (timer === 0 && timerType === "break") {
      setTimerType("session"); // Change timer type break
      setTimer(sessionLength * 60); // Multiply by 60 because we need to convert minutes into seconds
      playSound();
    }

    // This must be the clean-up function
    () => {
      if(timeoutId)
            clearTimeout(timeoutId);
    }
  }, [....]

如果您在其他任何地方使用 timeoutId,您还可以将其设置为状态。否则,不需要状态。

希望有效!

相关问题