Initial commit
This commit is contained in:
313
components/experiments/AudioElement.vue
Normal file
313
components/experiments/AudioElement.vue
Normal file
@@ -0,0 +1,313 @@
|
||||
<template>
|
||||
<div class="slider-wrapper">
|
||||
<!-- Slot for passing any audio element -->
|
||||
<slot />
|
||||
<div class="slider">
|
||||
<input
|
||||
id="gain-control"
|
||||
v-model.number="volumeValue"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.001"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"
|
||||
:title="tooltipTitle"
|
||||
@wheel.prevent="changeVolumeOnWheel"
|
||||
>
|
||||
<span
|
||||
class="slider-progress-bar"
|
||||
:style="{ width: `${volumeValue * 100}%` }"
|
||||
/>
|
||||
</div>
|
||||
<audio
|
||||
:id="title"
|
||||
ref="audioElement"
|
||||
hidden
|
||||
loop
|
||||
@loadedmetadata="handleUpdatedMetadata"
|
||||
@canplay="ready=true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { useAudioStore } from '~/stores/audio'
|
||||
import { useUserStore } from '~/stores/user'
|
||||
|
||||
export default {
|
||||
name: 'AudioElement',
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Unknown'
|
||||
},
|
||||
tooltipTitle: {
|
||||
type: String,
|
||||
default: 'Change the volume by click, scroll or touch.',
|
||||
required: false
|
||||
},
|
||||
volume: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
emits: ['update:volume', 'update:loaded', 'update:playing', 'update:canplay'],
|
||||
setup (props, { emit }) {
|
||||
const audioStore = useAudioStore()
|
||||
const audioElement = ref<HTMLAudioElement | null>(null)
|
||||
const volumeValue = ref(props.volume)
|
||||
const ready = ref(false)
|
||||
|
||||
const mute = () => {
|
||||
if ((audioElement.value instanceof HTMLAudioElement)) {
|
||||
const ae = audioElement.value
|
||||
ae.muted = true
|
||||
}
|
||||
}
|
||||
const play = async () => {
|
||||
try {
|
||||
const { $audioPrepared } = useNuxtApp()
|
||||
await $audioPrepared
|
||||
const sink = useUserStore().audioOutputDevice as MediaDeviceInfo
|
||||
audioElement.value?.setSinkId(sink.deviceId)
|
||||
if (audioElement.value instanceof HTMLAudioElement) {
|
||||
// set audio element to mute, will be anyway muted after connecting to the web audio graph
|
||||
audioElement.value.play()
|
||||
}
|
||||
} catch (error) {
|
||||
useNuxtApp().$logger.error('Oouh sorry! Error while setting up audio, please reload.', error)
|
||||
}
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
audioElement.value?.pause()
|
||||
}
|
||||
|
||||
const togglePlayPause = async () => {
|
||||
if (audioElement.value) {
|
||||
if (audioStore.playing) {
|
||||
await audioElement.value.play()
|
||||
} else {
|
||||
audioElement.value.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
watch(() => props.volume, (newValue) => {
|
||||
volumeValue.value = newValue
|
||||
})
|
||||
watch(() => audioStore.playing, async (newValue) => {
|
||||
if (newValue) {
|
||||
updateMediaSession()
|
||||
await togglePlayPause()
|
||||
} else {
|
||||
await togglePlayPause()
|
||||
}
|
||||
})
|
||||
|
||||
watch(volumeValue, (newValue) => {
|
||||
if (audioElement.value) {
|
||||
audioElement.value.volume = newValue
|
||||
emit('update:volume', newValue) // Gib die Änderungen an die Elternkomponente weiter
|
||||
}
|
||||
})
|
||||
|
||||
const changeVolumeOnWheel = (event:WheelEvent) => {
|
||||
// Adjust volume on wheel scroll
|
||||
const deltaY = event.deltaY
|
||||
if (deltaY < 0) {
|
||||
const volumeAdd = (Math.min(1, volumeValue.value + 0.02))
|
||||
volumeValue.value = volumeAdd
|
||||
} else {
|
||||
const volumeCut = (Math.max(0, volumeValue.value - 0.02))
|
||||
volumeValue.value = volumeCut
|
||||
}
|
||||
}
|
||||
|
||||
const updateMediaSession = () => {
|
||||
try {
|
||||
const path = window.location.origin + '/images/scenery/' + props.title.toLowerCase() + '.jpg'
|
||||
// useNuxtApp().$logger.log(navigator.mediaSession.metadata)
|
||||
if (audioElement.value) {
|
||||
audioElement.value.controls = true
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: props.title,
|
||||
artist: 'mindboost',
|
||||
album: 'mindboost Originale',
|
||||
artwork: [
|
||||
{ src: path, sizes: '192x192', type: 'image/jpeg' }
|
||||
]
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
// useNuxtApp().$logger.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
const handleUpdatedMetadata = () => {
|
||||
// use the navigator for the soundscape.
|
||||
if (props.title !== 'Noise') {
|
||||
updateMediaSession()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (audioElement.value) {
|
||||
audioElement.value.src = props.src
|
||||
audioElement.value.volume = volumeValue.value
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
audioElement.value = null
|
||||
volumeValue.value = 0
|
||||
})
|
||||
|
||||
return {
|
||||
audioElement,
|
||||
ready,
|
||||
play,
|
||||
pause,
|
||||
handleUpdatedMetadata,
|
||||
volumeValue,
|
||||
togglePlayPause,
|
||||
changeVolumeOnWheel,
|
||||
updateMediaSession
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.slider-wrapper{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
.slider-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 576px) {
|
||||
.slider{
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-progress-bar {
|
||||
position: absolute;
|
||||
height: 10px;
|
||||
background-color: #e9c046;
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-radius: 5px;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Allgemeine Einstellungen für den Slider */
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none; /* Entfernt das Standard-Styling in Webkit-Browsern */
|
||||
appearance: none; /* Entfernt Standard-Styling in anderen Browsern */
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index:1;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* Styling für den Track in Webkit-Browsern */
|
||||
input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: 10px;
|
||||
background: transparent;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Styling für den Thumb in Webkit-Browsern */
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
margin-top: -5px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
border: 2px solid #e9c046;
|
||||
}
|
||||
|
||||
/* Styling für den Track in Firefox */
|
||||
input[type="range"]::-moz-range-track {
|
||||
height: 10px;
|
||||
background: transparent;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Styling für den Thumb in Firefox */
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background-color: #e9c046;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border: 2px solid #e9c046;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-progress {
|
||||
height: 10px;
|
||||
background-color: #e9c046;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Styling für den Track in Internet Explorer/Edge */
|
||||
input[type="range"]::-ms-track {
|
||||
height: 10px;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: transparent;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Styling für den Thumb in Internet Explorer/Edge */
|
||||
input[type="range"]::-ms-thumb {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background: #f5f5f5;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
border: 2px solid #e9c046;
|
||||
}
|
||||
|
||||
/* Entfernt Ticks in IE */
|
||||
input[type="range"]::-ms-ticks-after,
|
||||
input[type="range"]::-ms-ticks-before {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user