fix(player): volume slider jumps / flickers
This commit is contained in:
@@ -13,7 +13,6 @@
|
|||||||
import { Progress } from '$lib/components/ui/progress';
|
import { Progress } from '$lib/components/ui/progress';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import duration from 'dayjs/plugin/duration';
|
import duration from 'dayjs/plugin/duration';
|
||||||
import { Slider } from '$lib/components/ui/slider';
|
|
||||||
import * as Popover from '$lib/components/ui/popover';
|
import * as Popover from '$lib/components/ui/popover';
|
||||||
import { Separator } from '$lib/components/ui/separator';
|
import { Separator } from '$lib/components/ui/separator';
|
||||||
import { getPlayerState } from '$lib/player.svelte';
|
import { getPlayerState } from '$lib/player.svelte';
|
||||||
@@ -77,19 +76,24 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const submitVolumeForm: SubmitFunction = async ({ formData }) => {
|
||||||
|
formData.set('volume', player.volume.toString());
|
||||||
|
|
||||||
|
return async ({ update, result }) => {
|
||||||
|
await update({
|
||||||
|
invalidateAll: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.type === 'success' && result.data && 'volume' in result.data) {
|
||||||
|
player.volume = result.data.volume;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
function onMouseMove(e: MouseEvent) {
|
function onMouseMove(e: MouseEvent) {
|
||||||
mouse.offsetX = e.offsetX;
|
mouse.offsetX = e.offsetX;
|
||||||
mouse.offsetY = e.offsetY;
|
mouse.offsetY = e.offsetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
let volume = $state({
|
|
||||||
get value() {
|
|
||||||
return [player.volume];
|
|
||||||
},
|
|
||||||
set value(v: number[]) {
|
|
||||||
player.volume = v[0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<footer class="border border-x-0 border-t border-border/40 bg-background/95 px-4">
|
<footer class="border border-x-0 border-t border-border/40 bg-background/95 px-4">
|
||||||
@@ -189,20 +193,23 @@
|
|||||||
class="flex-1"
|
class="flex-1"
|
||||||
method="POST"
|
method="POST"
|
||||||
action="/player?/volume"
|
action="/player?/volume"
|
||||||
use:enhance
|
use:enhance={submitVolumeForm}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="volume" value={volume.value[0]} />
|
<input
|
||||||
<Slider
|
class="volume-slider"
|
||||||
class="w-full"
|
type="range"
|
||||||
min={0}
|
min={0.0}
|
||||||
max={1}
|
max={1.0}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
bind:value={volume.value}
|
bind:value={player.volume}
|
||||||
onValueChange={() => {
|
oninput={() => {
|
||||||
volumeForm?.requestSubmit();
|
volumeForm?.requestSubmit();
|
||||||
}}
|
}}
|
||||||
onValueCommit={() => {
|
onmousedown={() => {
|
||||||
volumeForm?.requestSubmit();
|
player.adjustingVolume = true;
|
||||||
|
}}
|
||||||
|
onmouseup={() => {
|
||||||
|
player.adjustingVolume = false;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
@@ -240,3 +247,30 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<svelte:window onmousemove={onMouseMove} />
|
<svelte:window onmousemove={onMouseMove} />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.volume-slider {
|
||||||
|
-webkit-appearance: none !important; /* Override default CSS styles */
|
||||||
|
appearance: none !important;
|
||||||
|
width: 100%; /* Full-width */
|
||||||
|
height: 8px; /* Specified height */
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: hsl(var(--secondary));
|
||||||
|
outline: none; /* Remove outline */
|
||||||
|
opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */
|
||||||
|
-webkit-transition: 0.2s; /* 0.2 seconds transition on hover */
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume-slider::-webkit-slider-thumb {
|
||||||
|
@apply size-4 bg-background ring ring-secondary ring-offset-0 ring-offset-transparent transition-colors;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
background-color: hsl(var(--primary));
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume-slider::-webkit-slider-thumb:active {
|
||||||
|
@apply ring-offset-2 ring-offset-background;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
if (result.type === 'success' && result.data) {
|
if (result.type === 'success' && result.data) {
|
||||||
player.currentlyPlaying = result.data.track ?? null;
|
player.currentlyPlaying = result.data.track ?? null;
|
||||||
player.progress = result.data.position;
|
player.progress = result.data.position;
|
||||||
|
player.isPaused = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import { PlayerClient } from './proto/player.client';
|
|||||||
import { protoTransport } from '../hooks.client';
|
import { protoTransport } from '../hooks.client';
|
||||||
|
|
||||||
class PlayerState {
|
class PlayerState {
|
||||||
volume = $state(0);
|
volume = $state(0.0);
|
||||||
currentlyPlaying = $state<Track | null>(null);
|
currentlyPlaying = $state<Track | null>(null);
|
||||||
progress = $state<bigint>(0n);
|
progress = $state<bigint>(0n);
|
||||||
isPaused = $state(false);
|
isPaused = $state(false);
|
||||||
|
adjustingVolume = $state(false);
|
||||||
#abortContoller: AbortController | null = null;
|
#abortContoller: AbortController | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -26,7 +27,10 @@ class PlayerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyStatus(status: PlayerStatus) {
|
applyStatus(status: PlayerStatus) {
|
||||||
this.volume = status.volume;
|
if (!this.adjustingVolume) {
|
||||||
|
this.volume = status.volume;
|
||||||
|
}
|
||||||
|
|
||||||
this.progress = status.progress;
|
this.progress = status.progress;
|
||||||
this.currentlyPlaying = status.currentlyPlaying ?? null;
|
this.currentlyPlaying = status.currentlyPlaying ?? null;
|
||||||
this.isPaused = status.isPaused;
|
this.isPaused = status.isPaused;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { PlayerClient } from '$lib/proto/player.client';
|
|
||||||
import { protoTransport } from '../hooks.server';
|
|
||||||
import type { LayoutServerLoad } from './$types';
|
import type { LayoutServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: LayoutServerLoad = async () => {
|
export const load: LayoutServerLoad = async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user