feat: add next track info to enlarged cover view with animation
This commit is contained in:
parent
25c6f1e5fb
commit
8361d31408
5 changed files with 68 additions and 2 deletions
|
|
@ -43,6 +43,10 @@
|
||||||
<div class="fullscreen-track-info">
|
<div class="fullscreen-track-info">
|
||||||
<h2 id="fullscreen-track-title"></h2>
|
<h2 id="fullscreen-track-title"></h2>
|
||||||
<h3 id="fullscreen-track-artist"></h3>
|
<h3 id="fullscreen-track-artist"></h3>
|
||||||
|
<div id="fullscreen-next-track" style="display: none;">
|
||||||
|
<span class="label">Up Next: </span>
|
||||||
|
<span class="value"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fullscreen-controls">
|
<div class="fullscreen-controls">
|
||||||
<!-- Controls will be cloned or managed here if needed, or we just rely on main controls -->
|
<!-- Controls will be cloned or managed here if needed, or we just rely on main controls -->
|
||||||
|
|
|
||||||
10
js/app.js
10
js/app.js
|
|
@ -252,7 +252,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
clearLyricsPanelSync(audioPlayer, lyricsPanel);
|
clearLyricsPanelSync(audioPlayer, lyricsPanel);
|
||||||
}
|
}
|
||||||
} else if (mode === 'cover') {
|
} else if (mode === 'cover') {
|
||||||
ui.showFullscreenCover(player.currentTrack);
|
const nextTrack = player.getNextTrack();
|
||||||
|
ui.showFullscreenCover(player.currentTrack, nextTrack);
|
||||||
} else {
|
} else {
|
||||||
// Default to 'album' mode - navigate to album
|
// Default to 'album' mode - navigate to album
|
||||||
if (player.currentTrack.album?.id) {
|
if (player.currentTrack.album?.id) {
|
||||||
|
|
@ -315,6 +316,13 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update Fullscreen/Enlarged Cover if it's open
|
||||||
|
const fullscreenOverlay = document.getElementById('fullscreen-cover-overlay');
|
||||||
|
if (fullscreenOverlay && getComputedStyle(fullscreenOverlay).display !== 'none') {
|
||||||
|
const nextTrack = player.getNextTrack();
|
||||||
|
ui.showFullscreenCover(player.currentTrack, nextTrack);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('click', async (e) => {
|
document.addEventListener('click', async (e) => {
|
||||||
|
|
|
||||||
13
js/player.js
13
js/player.js
|
|
@ -366,6 +366,19 @@ export class Player {
|
||||||
return this.shuffleActive ? this.shuffledQueue : this.queue;
|
return this.shuffleActive ? this.shuffledQueue : this.queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNextTrack() {
|
||||||
|
const currentQueue = this.getCurrentQueue();
|
||||||
|
if (this.currentQueueIndex === -1 || currentQueue.length === 0) return null;
|
||||||
|
|
||||||
|
const nextIndex = this.currentQueueIndex + 1;
|
||||||
|
if (nextIndex < currentQueue.length) {
|
||||||
|
return currentQueue[nextIndex];
|
||||||
|
} else if (this.repeatMode === REPEAT_MODE.ALL) {
|
||||||
|
return currentQueue[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
updatePlayingTrackIndicator() {
|
updatePlayingTrackIndicator() {
|
||||||
const currentTrack = this.getCurrentQueue()[this.currentQueueIndex];
|
const currentTrack = this.getCurrentQueue()[this.currentQueueIndex];
|
||||||
document.querySelectorAll('.track-item').forEach(item => {
|
document.querySelectorAll('.track-item').forEach(item => {
|
||||||
|
|
|
||||||
16
js/ui.js
16
js/ui.js
|
|
@ -259,13 +259,14 @@ export class UIRenderer {
|
||||||
root.style.removeProperty('--ring');
|
root.style.removeProperty('--ring');
|
||||||
}
|
}
|
||||||
|
|
||||||
showFullscreenCover(track) {
|
showFullscreenCover(track, nextTrack) {
|
||||||
if (!track) return;
|
if (!track) return;
|
||||||
|
|
||||||
const overlay = document.getElementById('fullscreen-cover-overlay');
|
const overlay = document.getElementById('fullscreen-cover-overlay');
|
||||||
const image = document.getElementById('fullscreen-cover-image');
|
const image = document.getElementById('fullscreen-cover-image');
|
||||||
const title = document.getElementById('fullscreen-track-title');
|
const title = document.getElementById('fullscreen-track-title');
|
||||||
const artist = document.getElementById('fullscreen-track-artist');
|
const artist = document.getElementById('fullscreen-track-artist');
|
||||||
|
const nextTrackEl = document.getElementById('fullscreen-next-track');
|
||||||
|
|
||||||
const coverUrl = this.api.getCoverUrl(track.album?.cover, '1280');
|
const coverUrl = this.api.getCoverUrl(track.album?.cover, '1280');
|
||||||
|
|
||||||
|
|
@ -273,6 +274,19 @@ export class UIRenderer {
|
||||||
title.textContent = track.title;
|
title.textContent = track.title;
|
||||||
artist.textContent = track.artist?.name || 'Unknown Artist';
|
artist.textContent = track.artist?.name || 'Unknown Artist';
|
||||||
|
|
||||||
|
if (nextTrack) {
|
||||||
|
nextTrackEl.style.display = 'flex';
|
||||||
|
nextTrackEl.querySelector('.value').textContent = `${nextTrack.title} • ${nextTrack.artist?.name || 'Unknown'}`;
|
||||||
|
|
||||||
|
// Replay animation
|
||||||
|
nextTrackEl.classList.remove('animate-in');
|
||||||
|
void nextTrackEl.offsetWidth; // Trigger reflow
|
||||||
|
nextTrackEl.classList.add('animate-in');
|
||||||
|
} else {
|
||||||
|
nextTrackEl.style.display = 'none';
|
||||||
|
nextTrackEl.classList.remove('animate-in');
|
||||||
|
}
|
||||||
|
|
||||||
// Set the background image via CSS variable for the pseudo-element to use
|
// Set the background image via CSS variable for the pseudo-element to use
|
||||||
overlay.style.setProperty('--bg-image', `url('${coverUrl}')`);
|
overlay.style.setProperty('--bg-image', `url('${coverUrl}')`);
|
||||||
|
|
||||||
|
|
|
||||||
27
styles.css
27
styles.css
|
|
@ -1319,6 +1319,33 @@ input:checked + .slider::before {
|
||||||
text-shadow: 0 2px 10px rgba(0,0,0,0.5);
|
text-shadow: 0 2px 10px rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fullscreen-next-track {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
text-shadow: 0 1px 4px rgba(0,0,0,0.5);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
opacity: 0; /* Initially hidden for animation */
|
||||||
|
}
|
||||||
|
|
||||||
|
#fullscreen-next-track.animate-in {
|
||||||
|
animation: fadeIn 0.5s ease 0.2s forwards; /* Added forwards to keep opacity 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#fullscreen-next-track .label {
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fullscreen-next-track .value {
|
||||||
|
font-weight: 500;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
#fullscreen-cover-overlay {
|
#fullscreen-cover-overlay {
|
||||||
padding-bottom: 160px; /* Account for taller mobile player bar */
|
padding-bottom: 160px; /* Account for taller mobile player bar */
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue