UNPKG

react-h5-audio-player

Version:

A customizable React audio player. Written in TypeScript. Mobile compatible. Keyboard friendly

265 lines (264 loc) 9.37 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.ProgressBarForwardRef = exports.ProgressBar = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireWildcard(require("react")); var _utils = require("./utils"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } class ProgressBar extends _react.Component { timeOnMouseMove = 0; hasAddedAudioEventListener = false; state = { isDraggingProgress: false, currentTimePos: '0%', hasDownloadProgressAnimation: false, downloadProgressArr: [], waitingForSeekCallback: false }; getDuration() { const { audio, srcDuration } = this.props; return typeof srcDuration === 'undefined' ? audio.duration : srcDuration; } getCurrentProgress = event => { const { audio, progressBar } = this.props; const isSingleFileProgressiveDownload = audio.src.indexOf('blob:') !== 0 && typeof this.props.srcDuration === 'undefined'; if (isSingleFileProgressiveDownload && (!audio.src || !isFinite(audio.currentTime) || !progressBar.current)) { return { currentTime: 0, currentTimePos: '0%' }; } const progressBarRect = progressBar.current.getBoundingClientRect(); const maxRelativePos = progressBarRect.width; let relativePos = (0, _utils.getPosX)(event) - progressBarRect.left; if (relativePos < 0) { relativePos = 0; } else if (relativePos > maxRelativePos) { relativePos = maxRelativePos; } const duration = this.getDuration(); const currentTime = duration * relativePos / maxRelativePos; return { currentTime, currentTimePos: `${(relativePos / maxRelativePos * 100).toFixed(2)}%` }; }; handleContextMenu = event => { event.preventDefault(); }; handleMouseDownOrTouchStartProgressBar = event => { event.stopPropagation(); const { currentTime, currentTimePos } = this.getCurrentProgress(event.nativeEvent); if (isFinite(currentTime)) { this.timeOnMouseMove = currentTime; this.setState({ isDraggingProgress: true, currentTimePos }); if (event.nativeEvent instanceof MouseEvent) { window.addEventListener('mousemove', this.handleWindowMouseOrTouchMove); window.addEventListener('mouseup', this.handleWindowMouseOrTouchUp); } else { window.addEventListener('touchmove', this.handleWindowMouseOrTouchMove); window.addEventListener('touchend', this.handleWindowMouseOrTouchUp); } } }; handleWindowMouseOrTouchMove = event => { if (event instanceof MouseEvent) { event.preventDefault(); } event.stopPropagation(); const windowSelection = window.getSelection(); if (windowSelection && windowSelection.type === 'Range') { windowSelection.empty(); } const { isDraggingProgress } = this.state; if (isDraggingProgress) { const { currentTime, currentTimePos } = this.getCurrentProgress(event); this.timeOnMouseMove = currentTime; this.setState({ currentTimePos }); } }; handleWindowMouseOrTouchUp = event => { event.stopPropagation(); const newTime = this.timeOnMouseMove; const { audio, onChangeCurrentTimeError, onSeek } = this.props; if (onSeek) { this.setState({ isDraggingProgress: false, waitingForSeekCallback: true }, () => { onSeek(audio, newTime).then(() => this.setState({ waitingForSeekCallback: false }), err => { throw new Error(err); }); }); } else { const newProps = { isDraggingProgress: false }; if (audio.readyState === audio.HAVE_NOTHING || audio.readyState === audio.HAVE_METADATA || !isFinite(newTime)) { try { audio.load(); } catch (err) { newProps.currentTimePos = '0%'; return onChangeCurrentTimeError && onChangeCurrentTimeError(err); } } audio.currentTime = newTime; this.setState(newProps); } if (event instanceof MouseEvent) { window.removeEventListener('mousemove', this.handleWindowMouseOrTouchMove); window.removeEventListener('mouseup', this.handleWindowMouseOrTouchUp); } else { window.removeEventListener('touchmove', this.handleWindowMouseOrTouchMove); window.removeEventListener('touchend', this.handleWindowMouseOrTouchUp); } }; handleAudioTimeUpdate = (() => (0, _utils.throttle)(e => { const { isDraggingProgress } = this.state; const audio = e.target; if (isDraggingProgress || this.state.waitingForSeekCallback === true) return; const { currentTime } = audio; const duration = this.getDuration(); this.setState({ currentTimePos: `${(currentTime / duration * 100 || 0).toFixed(2)}%` }); }, this.props.progressUpdateInterval))(); handleAudioDownloadProgressUpdate = e => { const audio = e.target; const duration = this.getDuration(); const downloadProgressArr = []; for (let i = 0; i < audio.buffered.length; i++) { const bufferedStart = audio.buffered.start(i); const bufferedEnd = audio.buffered.end(i); downloadProgressArr.push({ left: `${Math.round(100 / duration * bufferedStart) || 0}%`, width: `${Math.round(100 / duration * (bufferedEnd - bufferedStart)) || 0}%` }); } clearTimeout(this.downloadProgressAnimationTimer); this.setState({ downloadProgressArr, hasDownloadProgressAnimation: true }); this.downloadProgressAnimationTimer = setTimeout(() => { this.setState({ hasDownloadProgressAnimation: false }); }, 200); }; initialize() { const { audio } = this.props; if (audio && !this.hasAddedAudioEventListener) { this.audio = audio; this.hasAddedAudioEventListener = true; audio.addEventListener('timeupdate', this.handleAudioTimeUpdate); audio.addEventListener('progress', this.handleAudioDownloadProgressUpdate); } } componentDidMount() { this.initialize(); } componentDidUpdate() { this.initialize(); } componentWillUnmount() { if (this.audio && this.hasAddedAudioEventListener) { this.audio.removeEventListener('timeupdate', this.handleAudioTimeUpdate); this.audio.removeEventListener('progress', this.handleAudioDownloadProgressUpdate); } clearTimeout(this.downloadProgressAnimationTimer); } render() { const { showDownloadProgress, showFilledProgress, progressBar, i18nProgressBar } = this.props; const { currentTimePos, downloadProgressArr, hasDownloadProgressAnimation } = this.state; return _react.default.createElement("div", { className: "rhap_progress-container", ref: progressBar, "aria-label": i18nProgressBar, role: "progressbar", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Number(currentTimePos.split('%')[0]), tabIndex: 0, onMouseDown: this.handleMouseDownOrTouchStartProgressBar, onTouchStart: this.handleMouseDownOrTouchStartProgressBar, onContextMenu: this.handleContextMenu }, _react.default.createElement("div", { className: `rhap_progress-bar ${showDownloadProgress ? 'rhap_progress-bar-show-download' : ''}` }, _react.default.createElement("div", { className: "rhap_progress-indicator", style: { left: currentTimePos } }), showFilledProgress && _react.default.createElement("div", { className: "rhap_progress-filled", style: { width: currentTimePos } }), showDownloadProgress && downloadProgressArr.map((_ref, i) => { let { left, width } = _ref; return _react.default.createElement("div", { key: i, className: "rhap_download-progress", style: { left, width, transitionDuration: hasDownloadProgressAnimation ? '.2s' : '0s' } }); }))); } } exports.ProgressBar = ProgressBar; const ProgressBarForwardRef = (props, ref) => _react.default.createElement(ProgressBar, (0, _extends2.default)({}, props, { progressBar: ref })); exports.ProgressBarForwardRef = ProgressBarForwardRef; var _default = exports.default = (0, _react.forwardRef)(ProgressBarForwardRef);