use-sound
Version:
### A React Hook for Sound Effects
175 lines (166 loc) • 5.92 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
function _interopNamespace(e) {
if (e && e.__esModule) { return e; } else {
var n = {};
if (e) {
Object.keys(e).forEach(function (k) {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
});
}
n['default'] = e;
return n;
}
}
var React = require('react');
var React__default = _interopDefault(React);
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function (n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
}
return n;
}, _extends.apply(null, arguments);
}
function _objectWithoutPropertiesLoose(r, e) {
if (null == r) return {};
var t = {};
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
if (-1 !== e.indexOf(n)) continue;
t[n] = r[n];
}
return t;
}
function useOnMount(callback) {
React.useEffect(callback, []);
}
var _excluded = ["id", "volume", "playbackRate", "soundEnabled", "interrupt", "onload"];
function useSound(src, _temp) {
var _ref = _temp === void 0 ? {} : _temp,
_ref$volume = _ref.volume,
volume = _ref$volume === void 0 ? 1 : _ref$volume,
_ref$playbackRate = _ref.playbackRate,
playbackRate = _ref$playbackRate === void 0 ? 1 : _ref$playbackRate,
_ref$soundEnabled = _ref.soundEnabled,
soundEnabled = _ref$soundEnabled === void 0 ? true : _ref$soundEnabled,
_ref$interrupt = _ref.interrupt,
interrupt = _ref$interrupt === void 0 ? false : _ref$interrupt,
onload = _ref.onload,
delegated = _objectWithoutPropertiesLoose(_ref, _excluded);
var HowlConstructor = React__default.useRef(null);
var isMounted = React__default.useRef(false);
var _React$useState = React__default.useState(null),
duration = _React$useState[0],
setDuration = _React$useState[1];
var _React$useState2 = React__default.useState(null),
sound = _React$useState2[0],
setSound = _React$useState2[1];
var handleLoad = function handleLoad() {
if (typeof onload === 'function') {
// @ts-ignore
onload.call(this);
}
if (isMounted.current) {
// @ts-ignore
setDuration(this.duration() * 1000);
}
// @ts-ignore
setSound(this);
};
// We want to lazy-load Howler, since sounds can't play on load anyway.
useOnMount(function () {
new Promise(function (resolve) { resolve(_interopNamespace(require('howler'))); }).then(function (mod) {
if (!isMounted.current) {
var _mod$Howl;
// Depending on the module system used, `mod` might hold
// the export directly, or it might be under `default`.
HowlConstructor.current = (_mod$Howl = mod.Howl) !== null && _mod$Howl !== void 0 ? _mod$Howl : mod["default"].Howl;
isMounted.current = true;
new HowlConstructor.current(_extends({
src: Array.isArray(src) ? src : [src],
volume: volume,
rate: playbackRate,
onload: handleLoad
}, delegated));
}
});
return function () {
isMounted.current = false;
};
});
// When the `src` changes, we have to do a whole thing where we recreate
// the Howl instance. This is because Howler doesn't expose a way to
// tweak the sound
React__default.useEffect(function () {
if (HowlConstructor.current && sound) {
setSound(new HowlConstructor.current(_extends({
src: Array.isArray(src) ? src : [src],
volume: volume,
onload: handleLoad
}, delegated)));
}
// The linter wants to run this effect whenever ANYTHING changes,
// but very specifically I only want to recreate the Howl instance
// when the `src` changes. Other changes should have no effect.
// Passing array to the useEffect dependencies list will result in
// ifinite loop so we need to stringify it, for more details check
// https://github.com/facebook/react/issues/14476#issuecomment-471199055
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(src)]);
// Whenever volume/playbackRate are changed, change those properties
// on the sound instance.
React__default.useEffect(function () {
if (sound) {
sound.volume(volume);
// HACK: When a sprite is defined, `sound.rate()` throws an error, because Howler tries to reset the "_default" sprite, which doesn't exist. This is likely a bug within Howler, but I don’t have the bandwidth to investigate, so instead, we’re ignoring playbackRate changes when a sprite is defined.
if (!delegated.sprite) {
sound.rate(playbackRate);
}
}
}, [sound, volume, playbackRate]);
var play = React__default.useCallback(function (options) {
if (typeof options === 'undefined') {
options = {};
}
if (!sound || !soundEnabled && !options.forceSoundEnabled) {
return;
}
if (interrupt) {
sound.stop();
}
if (options.playbackRate) {
sound.rate(options.playbackRate);
}
sound.play(options.id);
}, [sound, soundEnabled, interrupt]);
var stop = React__default.useCallback(function (id) {
if (!sound) {
return;
}
sound.stop(id);
}, [sound]);
var pause = React__default.useCallback(function (id) {
if (!sound) {
return;
}
sound.pause(id);
}, [sound]);
var returnedValue = [play, {
sound: sound,
stop: stop,
pause: pause,
duration: duration
}];
return returnedValue;
}
exports.default = useSound;
exports.useSound = useSound;
//# sourceMappingURL=use-sound.cjs.development.js.map