2022-04-15 14:24:30 +00:00
|
|
|
<template>
|
2023-12-22 14:53:29 +00:00
|
|
|
<footer
|
|
|
|
ref="root"
|
2024-04-04 22:20:42 +00:00
|
|
|
class="flex flex-col relative z-20 bg-k-bg-secondary h-k-footer-height"
|
2023-12-22 14:53:29 +00:00
|
|
|
@contextmenu.prevent="requestContextMenu"
|
|
|
|
@mousemove="showControls"
|
|
|
|
>
|
2022-12-23 15:44:34 +00:00
|
|
|
<AudioPlayer v-show="song" />
|
|
|
|
|
2024-04-04 22:20:42 +00:00
|
|
|
<div class="fullscreen-backdrop hidden" />
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2024-04-04 22:20:42 +00:00
|
|
|
<div class="wrapper relative flex flex-1">
|
2022-12-02 16:17:37 +00:00
|
|
|
<SongInfo />
|
|
|
|
<PlaybackControls />
|
|
|
|
<ExtraControls />
|
2022-04-15 14:24:30 +00:00
|
|
|
</div>
|
|
|
|
</footer>
|
|
|
|
</template>
|
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
<script lang="ts" setup>
|
2023-12-22 14:53:29 +00:00
|
|
|
import { throttle } from 'lodash'
|
2022-12-23 15:44:34 +00:00
|
|
|
import { computed, nextTick, ref, watch } from 'vue'
|
2022-10-30 23:13:57 +00:00
|
|
|
import { eventBus, isAudioContextSupported, requireInjection } from '@/utils'
|
2022-10-13 15:18:47 +00:00
|
|
|
import { CurrentSongKey } from '@/symbols'
|
2022-12-23 15:44:34 +00:00
|
|
|
import { artistStore, preferenceStore } from '@/stores'
|
2024-03-18 17:51:51 +00:00
|
|
|
import { audioService, playbackService } from '@/services'
|
2022-04-15 17:00:08 +00:00
|
|
|
|
2022-10-13 15:18:47 +00:00
|
|
|
import AudioPlayer from '@/components/layout/app-footer/AudioPlayer.vue'
|
|
|
|
import SongInfo from '@/components/layout/app-footer/FooterSongInfo.vue'
|
2022-04-20 15:57:53 +00:00
|
|
|
import ExtraControls from '@/components/layout/app-footer/FooterExtraControls.vue'
|
2022-10-13 15:18:47 +00:00
|
|
|
import PlaybackControls from '@/components/layout/app-footer/FooterPlaybackControls.vue'
|
2024-03-16 18:11:08 +00:00
|
|
|
import { useFullscreen } from '@vueuse/core'
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-12-23 15:44:34 +00:00
|
|
|
const song = requireInjection(CurrentSongKey, ref())
|
2023-12-22 14:53:29 +00:00
|
|
|
let hideControlsTimeout: number
|
2022-12-23 15:44:34 +00:00
|
|
|
|
|
|
|
const root = ref<HTMLElement>()
|
|
|
|
const artist = ref<Artist>()
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2022-04-15 17:00:08 +00:00
|
|
|
const requestContextMenu = (event: MouseEvent) => {
|
2023-12-22 14:53:29 +00:00
|
|
|
if (document.fullscreenElement) return
|
2022-10-13 15:18:47 +00:00
|
|
|
song.value && eventBus.emit('SONG_CONTEXT_MENU_REQUESTED', event, song.value)
|
2022-04-15 17:00:08 +00:00
|
|
|
}
|
2022-10-30 23:13:57 +00:00
|
|
|
|
2022-12-23 15:44:34 +00:00
|
|
|
watch(song, async () => {
|
|
|
|
if (!song.value) return
|
|
|
|
artist.value = await artistStore.resolve(song.value.artist_id)
|
|
|
|
})
|
|
|
|
|
2024-04-04 22:20:42 +00:00
|
|
|
const backgroundBackgroundImage = computed(() => {
|
2022-12-23 15:44:34 +00:00
|
|
|
const src = artist.value?.image ?? song.value?.album_cover
|
2024-04-04 22:20:42 +00:00
|
|
|
return src ? `url(${src})` : 'none'
|
2022-12-23 15:44:34 +00:00
|
|
|
})
|
|
|
|
|
2022-10-30 23:13:57 +00:00
|
|
|
const initPlaybackRelatedServices = async () => {
|
|
|
|
const plyrWrapper = document.querySelector<HTMLElement>('.plyr')
|
|
|
|
|
2024-03-18 17:51:51 +00:00
|
|
|
if (!plyrWrapper) {
|
2022-10-30 23:13:57 +00:00
|
|
|
await nextTick()
|
|
|
|
await initPlaybackRelatedServices()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
playbackService.init(plyrWrapper)
|
|
|
|
isAudioContextSupported && audioService.init(playbackService.player.media)
|
|
|
|
}
|
|
|
|
|
|
|
|
watch(preferenceStore.initialized, async initialized => {
|
|
|
|
if (!initialized) return
|
|
|
|
await initPlaybackRelatedServices()
|
|
|
|
}, { immediate: true })
|
2022-12-23 15:44:34 +00:00
|
|
|
|
2023-12-22 14:53:29 +00:00
|
|
|
const setupControlHidingTimer = () => {
|
|
|
|
hideControlsTimeout = window.setTimeout(() => root.value?.classList.add('hide-controls'), 5000)
|
|
|
|
}
|
|
|
|
|
|
|
|
const showControls = throttle(() => {
|
|
|
|
if (!document.fullscreenElement) return
|
|
|
|
|
|
|
|
root.value?.classList.remove('hide-controls')
|
|
|
|
window.clearTimeout(hideControlsTimeout)
|
|
|
|
setupControlHidingTimer()
|
|
|
|
}, 100)
|
|
|
|
|
2024-03-16 18:11:08 +00:00
|
|
|
const { isFullscreen, toggle: toggleFullscreen } = useFullscreen(root)
|
|
|
|
|
|
|
|
watch(isFullscreen, fullscreen => {
|
|
|
|
if (fullscreen) {
|
2024-04-04 22:20:42 +00:00
|
|
|
// setupControlHidingTimer()
|
2023-12-22 14:53:29 +00:00
|
|
|
root.value?.classList.remove('hide-controls')
|
2022-12-23 15:44:34 +00:00
|
|
|
} else {
|
2024-03-16 18:11:08 +00:00
|
|
|
window.clearTimeout(hideControlsTimeout)
|
2022-12-23 15:44:34 +00:00
|
|
|
}
|
|
|
|
})
|
2024-03-16 18:11:08 +00:00
|
|
|
|
|
|
|
eventBus.on('FULLSCREEN_TOGGLE', () => toggleFullscreen())
|
2022-04-15 14:24:30 +00:00
|
|
|
</script>
|
|
|
|
|
2024-04-04 20:13:35 +00:00
|
|
|
<style lang="postcss" scoped>
|
2022-04-15 14:24:30 +00:00
|
|
|
footer {
|
2022-10-13 15:18:47 +00:00
|
|
|
box-shadow: 0 0 30px 20px rgba(0, 0, 0, .2);
|
2022-12-23 15:44:34 +00:00
|
|
|
|
|
|
|
.fullscreen-backdrop {
|
2024-04-04 22:20:42 +00:00
|
|
|
background-image: v-bind(backgroundBackgroundImage);
|
2022-12-23 15:44:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
&:fullscreen {
|
|
|
|
padding: calc(100vh - 9rem) 5vw 0;
|
2024-04-04 22:20:42 +00:00
|
|
|
@apply bg-none;
|
2022-12-23 15:44:34 +00:00
|
|
|
|
2023-12-22 14:53:29 +00:00
|
|
|
&.hide-controls :not(.fullscreen-backdrop) {
|
2024-04-04 22:20:42 +00:00
|
|
|
transition: opacity 2s ease-in-out !important; /* overriding all children's custom transition, if any */
|
|
|
|
@apply opacity-0;
|
2023-12-22 14:53:29 +00:00
|
|
|
}
|
|
|
|
|
2022-12-23 15:44:34 +00:00
|
|
|
.wrapper {
|
2024-04-04 22:20:42 +00:00
|
|
|
@apply z-[3]
|
2022-12-23 15:44:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
&::before {
|
2024-04-04 22:20:42 +00:00
|
|
|
@apply bg-black bg-repeat absolute top-0 left-0 opacity-50 z-[1] pointer-events-none -m-[20rem];
|
|
|
|
content: '';
|
2022-12-23 15:44:34 +00:00
|
|
|
background-image: linear-gradient(135deg, #111 25%, transparent 25%),
|
|
|
|
linear-gradient(225deg, #111 25%, transparent 25%),
|
|
|
|
linear-gradient(45deg, #111 25%, transparent 25%),
|
|
|
|
linear-gradient(315deg, #111 25%, rgba(255, 255, 255, 0) 25%);
|
|
|
|
background-position: 6px 0, 6px 0, 0 0, 0 0;
|
|
|
|
background-size: 6px 6px;
|
|
|
|
width: calc(100% + 40rem);
|
|
|
|
height: calc(100% + 40rem);
|
|
|
|
transform: rotate(10deg);
|
|
|
|
}
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
background-image: linear-gradient(0deg, rgba(0, 0, 0, 1) 0%, rgba(255, 255, 255, 0) 30vh);
|
|
|
|
content: '';
|
2024-04-04 22:20:42 +00:00
|
|
|
@apply absolute w-full h-full top-0 left-0 z-[1] pointer-events-none;
|
2022-12-23 15:44:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.fullscreen-backdrop {
|
2024-04-04 22:20:42 +00:00
|
|
|
@apply saturate-[0.2] block absolute top-0 left-0 w-full h-full z-0 bg-cover bg-no-repeat bg-top;
|
2022-12-23 15:44:34 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-15 14:24:30 +00:00
|
|
|
}
|
|
|
|
</style>
|