You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Audio.js 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // @flow
  2. import React from 'react';
  3. import AbstractAudio from '../AbstractAudio';
  4. import type { AudioElement } from '../AbstractAudio';
  5. /**
  6. * The React/Web {@link Component} which is similar to and wraps around
  7. * {@code HTMLAudioElement} in order to facilitate cross-platform source code.
  8. */
  9. export default class Audio extends AbstractAudio {
  10. /**
  11. * Set to <code>true</code> when the whole file is loaded.
  12. */
  13. _audioFileLoaded: boolean;
  14. /**
  15. * Reference to the HTML audio element, stored until the file is ready.
  16. */
  17. _ref: ?AudioElement;
  18. /**
  19. * Creates new <code>Audio</code> element instance with given props.
  20. *
  21. * @param {Object} props - The read-only properties with which the new
  22. * instance is to be initialized.
  23. */
  24. constructor(props: Object) {
  25. super(props);
  26. // Bind event handlers so they are only bound once for every instance.
  27. this._onCanPlayThrough = this._onCanPlayThrough.bind(this);
  28. this._setRef = this._setRef.bind(this);
  29. }
  30. /**
  31. * Implements React's {@link Component#render()}.
  32. *
  33. * @inheritdoc
  34. * @returns {ReactElement}
  35. */
  36. render() {
  37. return (
  38. <audio
  39. loop = { Boolean(this.props.loop) }
  40. onCanPlayThrough = { this._onCanPlayThrough }
  41. preload = 'auto'
  42. // $FlowFixMe
  43. ref = { this._setRef }
  44. src = { this.props.src } />
  45. );
  46. }
  47. /**
  48. * Stops the audio HTML element.
  49. *
  50. * @returns {void}
  51. */
  52. stop() {
  53. if (this._ref) {
  54. this._ref.pause();
  55. // $FlowFixMe
  56. this._ref.currentTime = 0;
  57. }
  58. }
  59. /**
  60. * If audio element reference has been set and the file has been
  61. * loaded then {@link setAudioElementImpl} will be called to eventually add
  62. * the audio to the Redux store.
  63. *
  64. * @private
  65. * @returns {void}
  66. */
  67. _maybeSetAudioElementImpl() {
  68. if (this._ref && this._audioFileLoaded) {
  69. this.setAudioElementImpl(this._ref);
  70. }
  71. }
  72. _onCanPlayThrough: () => void;
  73. /**
  74. * Called when 'canplaythrough' event is triggered on the audio element,
  75. * which means that the whole file has been loaded.
  76. *
  77. * @private
  78. * @returns {void}
  79. */
  80. _onCanPlayThrough() {
  81. this._audioFileLoaded = true;
  82. this._maybeSetAudioElementImpl();
  83. }
  84. _setRef: (?AudioElement) => void;
  85. /**
  86. * Sets the reference to the HTML audio element.
  87. *
  88. * @param {HTMLAudioElement} audioElement - The HTML audio element instance.
  89. * @private
  90. * @returns {void}
  91. */
  92. _setRef(audioElement: ?AudioElement) {
  93. this._ref = audioElement;
  94. if (audioElement) {
  95. this._maybeSetAudioElementImpl();
  96. } else {
  97. // AbstractAudioElement is supposed to trigger "removeAudio" only if
  98. // it was previously added, so it's safe to just call it.
  99. this.setAudioElementImpl(null);
  100. // Reset the loaded flag, as the audio element is being removed from
  101. // the DOM tree.
  102. this._audioFileLoaded = false;
  103. }
  104. }
  105. }