在 React 应用程序中使用 <audio> 标签,在点击时播放所选文件

Using <audio> tags in React app, playing chosen file onClick

我可以映射一个数组并渲染一些图像(在 generateWord 函数的底部附近)。我希望能够单击图像并播放关联的音频文件。它播放数组中的第二个(或最后一个)音频文件。我想我应该将某种密钥或索引附加到音频文件,以便它知道播放哪一个,但我不确定具体如何操作。下面的代码是组件。部分状态在其下方进行了注释,因此您可以看到映射数组的来源。

const debug = require("debug")("components:Displayletter")
const React = require("react")
const { connect } = require("react-redux")
const request = require("superagent")
const { Link } = require("react-router")

class Displayletter extends React.Component {

  constructor() {
    super()
    this.playSound = this.playSound.bind(this)
  }

  playSound() {
    this.playWord.play()
  }

  handleClick(e) {
    e.preventDefault()
    this.props.router.push("/")
  }

  render() {
    debug(this.props)
    const { dispatch, letters, letter } = this.props
    const wordsArr = letter.wordImage

    return (
      <div className="row letter-container">
        <div className="col-sm-12">
          {this.generateWord(wordsArr)}
        </div>
      </div>
    )
  }

  generateWord(wordsArr) {
    return wordsArr.map((word) => {
      return (
        <div>
          <audio
          key={word.sound}
          ref={(x) => { this.playWord = x; }}>
            <source src={word.sound} preload='auto'/>
          </audio>
          <img src={word.image} onClick={this.playSound} />
        </div>
      )
    })
  }
}

module.exports = connect(state => state)(Displayletter)

//STATE
  // letters: [
  //   {
  //     id: 1,
  //     capital: "A",
  //     lowercase: "a",
  //     capitalSound: "/sounds/capitalSounds/A.mp3",
  //     lowerSound: "/sounds/lowerSounds/a.mp3",
  //     wordImage: [
  //       {id:1, image:"images/words/aniwaniwa.png", sound: "sounds/ua.mp3"},
  //       {id:2, image:"images/words/anuhe.png", sound: "sounds/anuhe.mp3"},
  //     ],
  //     multimedia: "/multimedia/aniwaniwa.webm",
  //     mediaName: "Aniwaniwa Song",
  //   },

第二次迭代 - 在构造函数中添加了一个空对象,并通过 onClick 和 playSound 传递了一个 'word' 参数。

generateWord(wordsArr) {
return wordsArr.map((word) => {
  return (
    <div>
    <audio
      key={word}
      ref={(x) => { this.playWords[word] = x; }}>
          <source src={word.sound} preload='auto'/>
    </audio>
      <img src={word.image} onClick={this.playSound.bind(this, word)} />
    </div>
  )
})

  playSound(word) {
    this.playWords[word].play()
  }

  constructor() {
    super()
    this.playCapital = this.playCapital.bind(this)
    this.playLower = this.playLower.bind(this)
    this.playSound = this.playSound.bind(this)
    this.playWords = {}
  }

***************第三轮******************

const debug = require("debug")("components:Displayletter")
const React = require("react")
const { connect } = require("react-redux")
const request = require("superagent")
const { Link } = require("react-router")

class Displayletter extends React.Component {

  constructor() {
    super()
    this.playCapital = this.playCapital.bind(this)
    this.playLower = this.playLower.bind(this)
    this.playWords = {}
  }

  playCapital() {
    this.playCap.play()
  }

  playLower() {
    this.playLow.play()
  }

  playSound(word) {
    this.playWords[word].play()
  }

  handleClick(e) {
    e.preventDefault()
    this.props.router.push("/")
  }



  render() {
debug(this.props)
const { dispatch, letters, letter } = this.props
const wordsArr = letter.wordImage

    return (
  <div className="row letter-container">
    <div className="col-sm-12">

      <audio
      key={letter.capitalSound}
      ref={(cap) => { this.playCap = cap; }}>
        <source
          src={letter.capitalSound}
          preload="auto" />
        <track
          kind="captions"
          src=""
          srcLang="en" />
      </audio>

      <audio
      key={letter.lowerSound}
      ref={(low) => { this.playLow = low; }}>
        <source
          src={letter.lowerSound}
          preload="auto" />
        <track
          kind="captions"
          src=""
          srcLang="en" />
      </audio>

      <button
        type="button"
        className="btn btn-xl display"
        onClick={this.playCapital}>
        {letter.capital}
      </button>

      <button
        type="button"
        className="btn btn-xl display"
        onClick={this.playLower}>
        {letter.lowercase}
      </button>

    </div>

      <div className="col-sm-12">
    {this.generateWord(wordsArr)}
  </div>

        <div className="col-sm-12">
      <Link key={letter.id} to={`/media/${letter.capital}`}>
        <button
          type="button"
          className="btn"
          onClick={() =>
              dispatch({
                type: "RENDER_LETTER",
                payload: letter,
              })
          }>
          Watch: {letter.mediaName}
        </button>
      </Link>

    </div>
  </div>
)
  }

  generateWord(wordsArr) {
    return wordsArr.map((word) => {
      return (
    <div>
          <audio
        key={word}
        ref={(x) => { this.playWords[word] = x; }}>
          <source src={word.sound} preload='auto'/>
          </audio>
          <img src={word.image} onClick={this.playSound.bind(this, word)} />
        </div>
      )
    })
  }
}

module.exports = connect(state => state)(Displayletter)

  // letters: [
  //   {
  //     id: 1,
  //     capital: "A",
  //     lowercase: "a",
  //     capitalSound: "/sounds/capitalSounds/A.mp3",
  //     lowerSound: "/sounds/lowerSounds/a.mp3",
  //     wordImage: [
  //       {id:1, image:"images/words/aniwaniwa.png", sound: "sounds/ua.mp3"},
  //       {id:2, image:"images/words/anuhe.png", sound: "sounds/anuhe.mp3"},
  //     ],
  //     multimedia: "/multimedia/aniwaniwa.webm",
  //     mediaName: "Aniwaniwa Song",
  //   },

你其实非常接近!问题在于您如何保存对音频元素的引用:ref={(x) => { this.playWord = x; }}。请注意,因为这是在 map 中, 每个 元素都在保存自己,一个接一个,作为 this.playWord,因此只有最后一个文件被播放当您点击时。

您要做的是将所有单词保存到一个对象或数组中,然后在您的点击处理程序中引用您要播放的单词。

首先,在构造函数中执行类似 this.playWords = {} 的操作,然后在 render 中执行 ref={(x) => { this.playWords[word] = x; }}。这将为您提供一个对象 playWords,它存储对所有由单词键入的音频元素的引用。

然后,给 playSound 一个单词参数,并确保 onClick 通过像 onClick={this.playSound.bind(this, word)}.

那样绑定它来传递该参数

我整理了一个简单示例来演示该基本结构;您应该能够轻松地将这些原则应用到您的组件中。

class Example extends React.Component {
  constructor() {
    super();
    this.words = {};
  }

  logClick(i) {
    console.log("user clicked on", this.words[i]);
  }

  render() {
    const wordsArr = "this is an example sentence".split(" ");
    return (
      <div className="row letter-container">
        <div className="col-sm-12">
          {this.generateWord(wordsArr)}
        </div>
      </div>
    )
  }

  generateWord(wordsArr) {
    return wordsArr.map((word) => {
      return (
        <div>
          <span
            key={word}
            ref={x => { this.words[word] = x; }}
            onClick={this.logClick.bind(this, word)}
          >
            {word}
          </span>
        </div>
      );
    })
  }
}

ReactDOM.render(<Example />, document.getElementById("container"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="container"/>