314 lines
7.2 KiB
Vue
314 lines
7.2 KiB
Vue
<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>
|