Add option for fullscreen covers

environments/review-docs-renov-b1i8ag/deployments/15025
wvffle 2022-11-04 07:03:34 +00:00 zatwierdzone przez Kasper Seweryn
rodzic 9efe5d18b1
commit ed9cb97ba6
3 zmienionych plików z 98 dodań i 82 usunięć

Wyświetl plik

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { QueueItemSource } from '~/types'
import { whenever, watchDebounced, useCurrentElement, useScrollLock } from '@vueuse/core'
import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscreen, useIdle, refAutoReset } from '@vueuse/core'
import { nextTick, ref, computed, watchEffect, onMounted } from 'vue'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import { useGettext } from 'vue3-gettext'
@ -148,6 +148,18 @@ const hideArtist = () => {
}
}
const cover = ref()
const { isFullscreen: fullscreen, enter, exit } = useFullscreen(cover)
const { idle } = useIdle(2000)
const showTrackInfo = refAutoReset(false, 5000)
whenever(currentTrack, () => (showTrackInfo.value = true))
const milkdrop = ref()
const loadRandomPreset = () => {
milkdrop.value?.loadRandomPreset()
}
enum CoverType {
COVER_ART,
MILK_DROP
@ -170,15 +182,27 @@ const coverType = ref(CoverType.COVER_ART)
class="ui basic segment"
>
<template v-if="currentTrack">
<div class="cover-container">
<div
ref="cover"
:class="['cover-container', { idle, fullscreen }]"
>
<div class="cover">
<img
v-if="coverType === CoverType.COVER_ART"
ref="cover"
alt=""
:src="$store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
>
<milk-drop v-else-if="coverType === CoverType.MILK_DROP" />
<template v-if="coverType === CoverType.COVER_ART">
<img
v-if="fullscreen"
class="cover-shadow"
:src="$store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
>
<img
ref="cover"
alt=""
:src="$store.getters['instance/absoluteUrl'](currentTrack.coverUrl)"
>
</template>
<milk-drop
v-else-if="coverType === CoverType.MILK_DROP"
ref="milkdrop"
/>
<div class="cover-buttons">
<button
@ -195,8 +219,26 @@ const coverType = ref(CoverType.COVER_ART)
>
<i class="icon image outline" />
</button>
<button
v-if="!fullscreen"
class="ui secondary button"
@click="enter"
>
<i class="icon expand arrows alternate" />
</button>
</div>
</div>
<Transition name="queue">
<div
v-if="fullscreen && (!idle || showTrackInfo)"
class="track-info"
@click="loadRandomPreset()"
>
<h1>{{ currentTrack.title }}</h1>
<h2>{{ currentTrack.artistName }} &mdash; {{ currentTrack.albumTitle }}</h2>
</div>
</Transition>
</div>
<h1 class="ui header">
<div class="content ellipsis">

Wyświetl plik

@ -2,100 +2,35 @@
import { useMilkDrop } from '~/composables/audio/visualizer'
import { onScopeDispose, ref } from 'vue'
import { refAutoReset, useFullscreen, useIdle, useRafFn, whenever } from '@vueuse/core'
import { useQueue } from '~/composables/audio/queue'
import { useRafFn } from '@vueuse/core'
const milkdrop = ref()
const canvas = ref()
const { isFullscreen: fullscreen, enter, exit } = useFullscreen(milkdrop)
const { visualizer, loadRandomPreset, render } = useMilkDrop(canvas)
const { currentTrack } = useQueue()
const { idle } = useIdle(2000)
const showTrackInfo = refAutoReset(false, 5000)
whenever(currentTrack, () => (showTrackInfo.value = true))
const { pause } = useRafFn(render)
onScopeDispose(() => {
exit()
pause()
if (visualizer.value) {
visualizer.value.loseGLContext()
}
})
defineExpose({
loadRandomPreset
})
</script>
<template>
<div
ref="milkdrop"
:class="['visualizer', { idle, fullscreen }]"
class="visualizer"
>
<canvas
ref="canvas"
@click="loadRandomPreset()"
/>
<Teleport to=".cover > .cover-buttons">
<button
v-if="!fullscreen"
class="ui secondary button"
@click="enter"
>
<i class="icon expand arrows alternate" />
</button>
</Teleport>
<Transition name="slide-down">
<div
v-if="fullscreen && (!idle || showTrackInfo)"
class="track-info"
@click="loadRandomPreset()"
>
<h1>{{ currentTrack.title }}</h1>
<h2>{{ currentTrack.artistName }} &mdash; {{ currentTrack.albumTitle }}</h2>
</div>
</Transition>
</div>
</template>
<style scoped lang="scss">
.visualizer {
&.idle {
cursor: none;
}
&.fullscreen {
.slide-down-enter-active,
.slide-down-leave-active {
transition: all 0.2s ease;
will-change: transform, opacity;
}
.slide-down-enter-from,
.slide-down-leave-to {
opacity: 0;
transform: translateY(5vh);
}
}
}
.track-info {
position: absolute;
z-index: 2;
bottom: 0;
left: 0;
width: 100%;
background: linear-gradient(to bottom, #0000, #000);
color: #fff;
text-align: left;
padding: 3em 1em 1em;
h1, h2 {
margin: 0;
}
}
</style>

Wyświetl plik

@ -249,6 +249,10 @@
// Wvffle's styles
.theme-light .cover-container.fullscreen {
background: #fff;
}
.component-queue {
#queue-grid {
display: grid;
@ -289,6 +293,10 @@
width: 50vh;
max-width: 90%;
&.idle {
cursor: none;
}
.cover {
height: 0;
width: 100%;
@ -302,10 +310,15 @@
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
margin: 0 auto;
}
canvas {
z-index: 1;
img {
max-width: 100vh;
max-height: 100vh;
}
&:hover .cover-buttons {
@ -313,6 +326,12 @@
transform: translateY(0);
}
.cover-shadow {
transform: scale(1.2);
filter: blur(15vh);
border-radius: 22vh;
}
.cover-buttons {
position: absolute;
bottom: 1em;
@ -347,6 +366,26 @@
}
}
}
.track-info {
position: absolute;
z-index: 3;
bottom: 0;
left: 0;
width: 100%;
background: linear-gradient(to bottom, #0000, #000);
color: #fff;
text-align: left;
padding: 3em 1em 1em;
h1, h2 {
margin: 0;
}
}
&:not(.fullscreen) .track-info {
transition: none !important;
}
}
.progress-wrapper {