0.1.2 • Published 5 years ago

vue-renderless-audio v0.1.2

Weekly downloads
1
License
MIT
Repository
github
Last release
5 years ago

vue-renderless-audio

A completely renderless Vue (2.x) component to help you build custom audio players.

⚠️ This component is still in heavy development and should not be used in any production environment (or at your own risk)!

TOC

What does "renderless" mean?

A renderless component only provides functionality and leaves you in full control over markup and styling.

That said, it's important to understand that this component does not provide any UI whatsoever!

Install

# npm
$ npm install vue-renderless-audio --save

# yarn
$ yarn add vue-renderless-audio

Setup

Register the component either globally or locally

// Global registration
import Vue from 'vue'
import RenderlessAudio from 'vue-renderless-audio'
Vue.component('renderless-audio', RenderlessAudio)
// Local registration
import RenderlessAudio from 'vue-renderless-audio'
export default {
    components: {
        RenderlessAudio
    },
    
    ...
}

Basic usage in your markup looks like this

<renderless-audio src="/my-audio-file.mp3">
    <template slot-scope="{ controls, time, state }">

        <!-- Start building your UI here -->

    </template>    
</renderless-audio>

Example implementation

This snippet only shows the general idea behind this component and does not include any real styling yet:

<template>
    <renderless-audio 
        :src="sources" 
        :autoplay="true"
        :loop="true"
        :volume.sync="globalVolume"
        @canplaythrough="doSomething()">

        <div slot-scope="{ controls, time, state }" class="my-music-player">
            <!--
                Progress bar as a range slider, bind progress as value and
                listen for the input event to seek to a different position -->
            <input 
                type="range" 
                :value="state.progress" 
                @input="controls.seek($event.target.value)"
                min="0" max="100" step="0.1" />

            <!-- 
                Control playback with provided functions in the 'controls' object.
                Conditionally display Play or Pause, depending on the current state -->
            <button v-if="!state.isPlaying" @click="controls.play()">Play</button>
            <button v-else @click="controls.pause()">Pause</button>

            <!-- Mute/Unmute control -->
            <button @click="controls.toggleMuted()">Mute/Unmute</button>

            <!-- 
                Volume control as a range slider, bind current volume as value and 
                listen for the input event to set it -->
            <input
                type="range" 
                :value="state.volume" 
                @input="controls.setVolume($event.target.value)" 
                min="0" max="1" step="0.1" />

        </div>

    </renderless-audio>
</template>
<script>
import RenderlessAudio from 'vue-renderless-audio'
export default {
    components: {
        RenderlessAudio
    },

    data() {
        return {
            // Control volume globally from parent (using .sync-modifier on prop)
            globalVolume: 0.5,
        }
    }

    computed: {
        sources() {
            return [
                { src: '/some-audio-file.mp3', type: 'audio/mp3' },
                { src: '/some-audio-file.wav', type: 'audio/wav' }
            ]
        }
    }
}
</script>

Documentation

Props

src (required) The source of your audio file as a string or as an object array for multiple file types (containing src and type properties).

  • type: String or Array
  • default: null
<!-- Single file type -->
<renderless-audio src="/some-audio-file.mp3"></renderless-audio>

<!-- Multiple file types -->
<renderless-audio :src="[
    { src: '/some-audio-file.mp3', type: 'audio/mp3' },
    { src: '/some-audio-file.ogg', type: 'audio/ogg' }
]"></renderless-audio>

muted Sets player muted attribute

  • type: Boolean
  • default: false

autoplay Sets player autoplay attribute

  • type: Boolean
  • default: false

    Be aware of browsers autoplay policies. In most modern browsers, some kind of user action (eg. click) is required to autoplay media.

loop Sets player loop attribute

  • type: Boolean
  • default: false

volume Sets player volume, must be a floating digit between 0 and 1. (eg. 0.75 stands for 75% volume)

  • type: Number
  • default: 1

playbackRate Sets the speed at which the media is being played back. The normal playback rate is multiplied by this value to obtain the current rate, so a value of 1.0 indicates normal speed.

  • type: Number
  • default: 1

start Offsets the position where playback begins (in seconds). Must be greater than or equals 0.

  • type: Number
  • default: 0

preload Sets the player preload attribute. Hints to the browser how or if to preload data. Must be 'none' 'metadata' or 'auto'

  • type: `String
  • default: 'auto'

crossorigin Sets the player crossorigin attribute. Lets you configure the CORS requests for the element's fetched data. Must be 'anonymous' or 'use-credentials'

  • type: `String
  • default: null

native Shows the native audio player if set to true. Most likely you would only want to use this for development and debugging purposes.

  • type: Boolean
  • default: false

Sync props back to parent

For some props it's useful to have "two-way-binding". You can use the .sync modifier to automatically listen and update the parent data property. (Vue documentation: .sync modifier)

<renderless-audio 
    src="/my-audio-file.mp3" 
    :muted.sync="noSound">
</renderless-audio>
export default {
    ...
    data() {
        return {
            /* will be passed as a prop but updates itself 
               if 'muted' state inside component changes */
            noSound: true
        }
    }
    ...
}

Slot

All your markup goes into the default slot.

<renderless-audio src="/my-audio-file.mp3">
    <template slot-scope="{ controls, time, state }">

        <!-- Default slot: Your custom markup -->

    </template>    
</renderless-audio>

The slot scope exposes objects controls, time, state and el containing all necessary info and methods to control playback:

controls

MethodDescription
controls.play()Starts playback
controls.pause()Pauses playback
controls.stop()Pauses playback and sets the position back to the beginning
controls.togglePlay()Starts or pauses playback, depending on the current state
controls.mute()Mutes audio
controls.unmute()Unmutes audio
controls.toggleMuted()Mutes or unmutes audio, depending on the current state
controls.setVolume(volume)Sets the volume (volume: floating number between 0 and 1)
controls.seek(progress)Sets playback position to the given percentage value (progress: Number between 0 and 100)
controls.toggleLooping()Activates or deactivates looping of the audio, depending on the current state
controls.setPlaybackRate(rate)Sets playback rate
controls.reload()Stops playback and reloads the audio file
controls.download()Opens/downloads the audio source file

time

Property / MethodTypeDescription
time.playedNumberThe time played in seconds
time.remainingNumberThe time remaining in seconds
time.durationNumberThe total duration of the audio file in seconds
time.format(seconds)FunctionHelper function to convert seconds into a human readable format (hh:mm:ss)

state

PropertyTypeDescription
state.isReadyBooleanIs true if player is ready for playback (first frames loaded)
state.isWaitingBooleanIs true if player is buffering and waiting for data
state.isMutedBooleanIs true if player is muted
state.isLoopingBooleanIs true if looping is active
state.isLoadedBooleanIs true if the metadata is loaded
state.hasEndedBooleanIs true if playback has reached the end and is not looping
state.progressNumberCurrent progress of playback in percent
state.volumeNumberCurrent volume
state.playbackRateNumberCurrent playback speed
state.bufferedArrayAn array of all buffered timeranges, containing info about start, end and width (length) of buffered range. All numbers are percentages.
state.currentSourceStringCurrently loaded source file

el

The el object allows direct acces to the <audio> element.

Events

All native DOM events (Media events) of the underlying <audio> element are passed to the root of the component. Which means you can listen for native events directly on the <renderless-audio> component (Without the .native modifier!).

<renderless-audio
    src="/my-audio-file.mp3"
    @loadeddata="doSomething()"
    @canplaythrough="doSomethingElse()">
    ...
</renderless-audio>

Additional events In addition to the native media events, the component emits a few more useful events. Event | Data | Description --- | --- | --- init| Object | Fires when component is mounted and has been initialized. source-changed| Object | Fires when the source file changed. (Also passes the new source as source to the listener) stopped| Object | Fires when playback has stopped after calling controls.stop() looped| Object | Fires when playback has reached the end and looping is active

All of these events pass an object with (at least) the target and controls fields to the listener:

{
    // The `<audio>` element itself
    target: HTMLAudioElement

    // All functions described in the slot-scope 'controls' section
    controls: Array<Function>

    ...
}

This can be very useful if you want to directly react to certain events. In the following example we're immediately starting playback after the source has changed and muting the sound after the first loop:

<renderless-audio
    :src="myDynamicSource"
    @source-changed="$event.controls.play()"
    @looped="$event.controls.mute()">
    ...
</renderless-audio>

Prop update events You can listen to prop update events with @update:propName ('propName' being the actual name of the prop) or simply use the .sync modifier. (See: Sync props back to parent)

Access player controls from outside

Usually, all method calls to the player should happen inside your markup in the default slot. Although not really recommended, there are a few ways to call the control methods from outside:

Control methods are passed on the init event

<template>
    <renderless-audio
        @init="playerControls = $event.controls"
        src="/some-audio-file.mp3">
    </renderless-audio>

    <button @click="someActionOnParent()">Button outside slot</button>
</template>
<script>
export default {
    ...

    data() {
        return {
            playerControls: null
        }
    }

    methods: {
        someActionOnParent() {
            this.playerControls.seek(25)
            this.playerControls.play()
        }
    }

    ...
}
</script>

or

Make a reference to the component

<template>
    <renderless-audio
        ref="audioPlayer"
        src="/some-audio-file.mp3">
    </renderless-audio>

    <button @click="someActionOnParent()">Button outside slot</button>
</template>
<script>
export default {
    ...

    methods: {
        someActionOnParent() {
            this.$refs.audioPlayer.seek(25)
            this.$refs.audioPlayer.play()
        }
    }

    ...
}
</script>

Browser Support

This component has not been properly tested yet, but I'm planning to target all browsers supporting Vue 2.x and the <audio> element.

License

MIT