From 6be141d6af2892f0be0ca75f496ad711cd1f7bca Mon Sep 17 00:00:00 2001 From: Joshua Comeau Date: Fri, 4 Jun 2021 12:43:41 -0400 Subject: [PATCH] Improve performance by ditching 'isPlaying' --- README.md | 14 ++++++-------- src/index.ts | 31 +++++-------------------------- src/types.d.ts | 1 - src/types.ts | 2 +- stories/demos/Checkbox.js | 6 +++--- stories/demos/ShowWhilePlaying.js | 24 ++++++++++++++++++++++++ stories/use-sound.stories.tsx | 15 +++++++++++---- 7 files changed, 50 insertions(+), 43 deletions(-) create mode 100644 stories/demos/ShowWhilePlaying.js diff --git a/README.md b/README.md index 9cc6676..df1b74a 100644 --- a/README.md +++ b/README.md @@ -253,17 +253,15 @@ const [play, exposedData] = useSound('/meow.mp3'); // ^ What we're talking about ``` -| Name | Value | -| --------- | -------------------------------- | -| stop | function ((id?: string) => void) | -| pause | function ((id?: string) => void) | -| isPlaying | boolean | -| duration | number (or null) | -| sound | Howl (or null) | +| Name | Value | +| -------- | -------------------------------- | +| stop | function ((id?: string) => void) | +| pause | function ((id?: string) => void) | +| duration | number (or null) | +| sound | Howl (or null) | - `stop` is a function you can use to pre-emptively halt the sound. - `pause` is like `stop`, except it can be resumed from the same point. Unless you know you'll want to resume, you should use `stop`; `pause` hogs resources, since it expects to be resumed at some point. -- `isPlaying` lets you know whether this sound is currently playing or not. When the sound reaches the end, or it's interrupted with `stop` or `paused`, this value will flip back to `false`. You can use this to show some UI only while the sound is playing. - `duration` is the length of the sample, in milliseconds. It will be `null` until the sample has been loaded. Note that for sprites, it's the length of the entire file. - `sound` is an escape hatch. It grants you access to the underlying `Howl` instance. See the [Howler documentation](https://github.com/goldfire/howler.js) to learn more about how to use it. Note that this will be `null` for the first few moments after the component mounts. diff --git a/src/index.ts b/src/index.ts index db4e7a6..6da50f7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { HookOptions, PlayOptions, PlayFunction, ReturnedValue } from './types'; export default function useSound( src: string | string[], { + id, volume = 1, playbackRate = 1, soundEnabled = true, @@ -18,7 +19,6 @@ export default function useSound( const HowlConstructor = React.useRef(null); const isMounted = React.useRef(false); - const [isPlaying, setIsPlaying] = React.useState(false); const [duration, setDuration] = React.useState(null); const [sound, setSound] = React.useState(null); @@ -33,6 +33,9 @@ export default function useSound( // @ts-ignore setDuration(this.duration() * 1000); } + + // @ts-ignore + setSound(this); }; // We want to lazy-load Howler, since sounds can't play on load anyway. @@ -42,15 +45,13 @@ export default function useSound( HowlConstructor.current = mod.Howl; isMounted.current = true; - const sound = new HowlConstructor.current({ + new HowlConstructor.current({ src: Array.isArray(src) ? src : [src], volume, rate: playbackRate, onload: handleLoad, ...delegated, }); - - setSound(sound); } }); @@ -115,19 +116,6 @@ export default function useSound( } sound.play(options.id); - - if (isMounted.current) { - sound.once('end', () => { - // If sound is not looping - if (!sound.playing()) { - setIsPlaying(false); - } - }); - } - - if (isMounted.current) { - setIsPlaying(true); - } }, [sound, soundEnabled, interrupt] ); @@ -138,10 +126,6 @@ export default function useSound( return; } sound.stop(id); - - if (isMounted.current) { - setIsPlaying(false); - } }, [sound] ); @@ -152,10 +136,6 @@ export default function useSound( return; } sound.pause(id); - - if (isMounted.current) { - setIsPlaying(false); - } }, [sound] ); @@ -166,7 +146,6 @@ export default function useSound( sound, stop, pause, - isPlaying, duration, }, ]; diff --git a/src/types.d.ts b/src/types.d.ts index 2c2058e..3383a7c 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -20,7 +20,6 @@ export interface ExposedData { sound: Howl | null; stop: (id?: string) => void; pause: (id?: string) => void; - isPlaying: boolean; duration: number | null; } export declare type ReturnedValue = [PlayFunction, ExposedData]; diff --git a/src/types.ts b/src/types.ts index 4f10ba0..89656b2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,6 +3,7 @@ export type SpriteMap = { }; export type HookOptions = T & { + id?: string; volume?: number; playbackRate?: number; interrupt?: boolean; @@ -23,7 +24,6 @@ export interface ExposedData { sound: Howl | null; stop: (id?: string) => void; pause: (id?: string) => void; - isPlaying: boolean; duration: number | null; } diff --git a/stories/demos/Checkbox.js b/stories/demos/Checkbox.js index 0e28151..4e3bd36 100644 --- a/stories/demos/Checkbox.js +++ b/stories/demos/Checkbox.js @@ -13,9 +13,9 @@ const BORDER_WIDTH = 2; function CheckboxDemo() { const [isChecked, setIsChecked] = React.useState(false); - const [playActive] = useSound(popDown, { volume: 0.25 }); - const [playOn] = useSound(popUpOn, { volume: 0.25 }); - const [playOff] = useSound(popUpOff, { volume: 0.25 }); + const [playActive] = useSound(popDown, { id: 'active', volume: 0.25 }); + const [playOn] = useSound(popUpOn, { id: 'on', volume: 0.25 }); + const [playOff] = useSound(popUpOff, { id: 'off', volume: 0.25 }); return ( { + const [isPlaying, setIsPlaying] = React.useState(false); + + const [playBoop] = useSound(dunDunDunSfx, { + onplay: () => setIsPlaying(true), + onend: () => setIsPlaying(false), + }); + + return ( + <> +

Is playing: {isPlaying.toString()}

+
+ + + ); +}; + +export default ShowWhilePlaying; diff --git a/stories/use-sound.stories.tsx b/stories/use-sound.stories.tsx index 323bfff..be76beb 100644 --- a/stories/use-sound.stories.tsx +++ b/stories/use-sound.stories.tsx @@ -9,6 +9,7 @@ import HoverDemo from './demos/Hover'; import RisingDemo from './demos/Rising'; import DrumMachineDemo from './demos/DrumMachine'; import MultipleSourcesDemo from './demos/SimpleMultipleSources'; +import ShowWhilePlayingDemo from './demos/ShowWhilePlaying'; import 'focus-visible'; @@ -28,8 +29,6 @@ Simple.story = { export const Checkbox = () => (
- -
); @@ -64,12 +63,20 @@ DrumMachine.story = { export const MultipleSources = () => { const options = { 'wav/mp3': 'wav_mp3', - 'mp3/wav': 'mp3_wav' + 'mp3/wav': 'mp3_wav', }; const value = radios('Source', options, 'wav_mp3', 'group1'); - return ; + return ; }; MultipleSources.story = { name: 'Multiple sources support', }; + +export const ShowWhilePlaying = () => { + return ; +}; + +ShowWhilePlaying.story = { + name: 'With Howler events (show while playing)', +};