[READ-ONLY] Mirror of https://github.com/probablykasper/ferrum. Music library app for Mac, Linux and Windows ferrum.kasper.space
electron linux macos music music-library music-player napi windows
0

Configure Feed

Select the types of activity you want to include in your feed.

Optimize slider performance

Kasper (Sep 12, 2024, 2:08 AM +0200) 8b41cf5e e057f8dc

+50 -56
+3 -3
src/components/Player.svelte
··· 179 179 <div class="time-bar"> 180 180 <small class="current-time">{getDuration($currentTime)}</small> 181 181 <Slider 182 - class="w-full" 182 + class="mx-1.5 w-full" 183 183 value={$currentTime / $duration || 0} 184 184 max={1} 185 - step={0.0001} 185 + update_on_drag={false} 186 186 on_apply={(value) => { 187 187 seek(value * $duration) 188 188 }} ··· 233 233 </svg> 234 234 {/if} 235 235 </button> 236 - <Slider class="mr-1 w-[100px]" bind:value={$volume} min={0} max={1} step={0.01} /> 236 + <Slider class="mr-2 w-[110px]" bind:value={$volume} max={1} /> 237 237 <button 238 238 tabindex="-1" 239 239 on:mousedown|preventDefault
+47 -53
src/components/Slider.svelte
··· 1 1 <script lang="ts"> 2 2 import type { HTMLBaseAttributes } from 'svelte/elements' 3 3 export let value: number 4 - export let min = 0 5 4 export let max = 100 6 - export let step: number | undefined = undefined 7 5 export let update_on_drag = true 8 6 export let on_apply: (value: number) => void = () => {} 9 7 export let klass = '' ··· 12 10 // eslint-disable-next-line @typescript-eslint/no-unused-vars 13 11 interface $$Props extends HTMLBaseAttributes { 14 12 value: number 15 - min?: number 16 13 max?: number 17 14 step?: number 18 15 class?: string 16 + update_on_drag?: boolean 19 17 on_apply?: (value: number) => void 20 18 } 21 19 22 - let element: HTMLInputElement 20 + let bar: HTMLDivElement 23 21 let dragging = false 24 22 25 23 let internal_value = value 26 - $: if (element && (update_on_drag || !dragging)) { 27 - internal_value = value 28 - element.value = String(value) 24 + $: if (update_on_drag || !dragging) { 25 + // Only update if the difference is 0.5px+ 26 + const diff_px = Math.abs(internal_value - value) * bar?.clientWidth * devicePixelRatio 27 + if (diff_px > 0.5) { 28 + internal_value = value 29 + } else if (!bar) { 30 + internal_value = value 31 + } 32 + } 33 + 34 + function apply(e: MouseEvent) { 35 + const delta = e.clientX - bar.getBoundingClientRect().left 36 + internal_value = Math.min(max, Math.max(0, (delta / bar.clientWidth) * max)) 37 + if (update_on_drag || !dragging) { 38 + value = internal_value 39 + } 29 40 } 30 41 </script> 31 42 43 + <svelte:window 44 + on:mousemove={(e) => { 45 + if (dragging) { 46 + apply(e) 47 + } 48 + }} 49 + on:mouseup={(e) => { 50 + if (dragging) { 51 + dragging = false 52 + apply(e) 53 + on_apply(value) 54 + } 55 + }} 56 + /> 57 + <!-- We use custom mouse events because updating an <input> value causes reflow --> 32 58 <div class="slider{` ${klass}`.trimEnd()}" {...$$restProps}> 59 + <!-- svelte-ignore a11y-no-static-element-interactions --> 33 60 <div 34 - class="group relative flex h-5 w-full items-center justify-center py-2 px-1" 35 - style:--slider-value="{((min + internal_value) / max) * 100}%" 61 + class="group flex h-5 w-full items-center justify-center py-2 px-1" 62 + on:mousedown={(e) => { 63 + apply(e) 64 + dragging = true 65 + }} 36 66 > 37 - <input 38 - bind:this={element} 39 - type="range" 40 - value={internal_value} 41 - {min} 42 - {max} 43 - {step} 44 - class="px-inherit absolute h-5 w-full appearance-none rounded-full border-none bg-transparent px-[inherit]" 45 - on:focus={(e) => { 46 - if (e.relatedTarget instanceof HTMLElement) { 47 - e.relatedTarget.focus() 48 - } else { 49 - e.currentTarget.blur() 50 - } 51 - }} 52 - on:input={(e) => { 53 - internal_value = Number(e.currentTarget.value) 54 - if (update_on_drag) { 55 - value = internal_value 56 - } 57 - }} 58 - on:mousedown={() => (dragging = true)} 59 - on:mouseup={(e) => { 60 - dragging = false 61 - value = Number(e.currentTarget.value) 62 - on_apply(value) 63 - }} 64 - /> 65 - <div class="pointer-events-none relative w-full"> 67 + <div class="pointer-events-none relative w-full rounded-full bg-gray-700" bind:this={bar}> 66 68 <div class="w-full overflow-hidden rounded-full"> 67 69 <div 68 - class="relative -left-full h-1 w-full rounded-full rounded-full bg-gray-300 transition duration-100 will-change-transform group-hover:bg-[hsl(217,100%,60%)]" 69 - style:translate="var(--slider-value)" 70 + class="relative -left-full h-1 w-full rounded-full bg-gray-300 transition-colors duration-100 will-change-transform group-hover:bg-[hsl(217,100%,60%)] group-active:bg-[hsl(217,100%,60%)]" 71 + style:translate="{(internal_value / max) * 100}%" 70 72 ></div> 71 73 </div> 72 - <div class="absolute top-0 flex size-full items-center" style:translate="var(--slider-value)"> 74 + <div 75 + class="absolute top-0 flex size-full items-center will-change-transform" 76 + style:translate="{(internal_value / max) * 100}%" 77 + > 73 78 <div 74 - class="thumb size-2.5 -translate-x-[50%] scale-[0.4] rounded-full bg-gray-300 opacity-0 shadow-md transition duration-75 group-hover:scale-100 group-hover:opacity-100" 79 + class="thumb size-2.5 -translate-x-[50%] scale-[0.4] rounded-full bg-gray-300 opacity-0 transition duration-75 group-hover:scale-100 group-hover:opacity-100 group-active:scale-100 group-active:opacity-100" 75 80 ></div> 76 81 </div> 77 82 </div> ··· 86 91 } 87 92 .thumb { 88 93 box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.5); 89 - } 90 - ::-webkit-slider-runnable-track { 91 - background-color: var(--color-gray-700); 92 - border-radius: 100px; 93 - height: 4px; 94 - } 95 - input::-webkit-slider-thumb { 96 - appearance: none; 97 - opacity: 0; 98 - width: 0px; 99 - height: 0px; 100 94 } 101 95 </style>