From 2d95c5e62ba2c491af4e3443461e4eeee2582b5f Mon Sep 17 00:00:00 2001 From: WardPearce Date: Tue, 10 Feb 2026 02:26:31 +1300 Subject: [PATCH] Improved player timeline tooltip --- materialious/src/lib/components/Player.svelte | 86 ++++++++++++------- materialious/src/lib/css/global.css | 8 ++ 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/materialious/src/lib/components/Player.svelte b/materialious/src/lib/components/Player.svelte index 3e819dad..64b45c1c 100644 --- a/materialious/src/lib/components/Player.svelte +++ b/materialious/src/lib/components/Player.svelte @@ -120,6 +120,7 @@ let playerSliderDebounce: ReturnType; let playerVolumeElement: HTMLElement | undefined = $state(); let playerIsFullscreen: boolean = $state(false); + let playerTimelineTooltip: HTMLDivElement | undefined = $state(); let playerInitalInteract = true; const playerTimelineSlider = new Slider({ @@ -1099,26 +1100,41 @@ } let requestAnimationTooltip: number | undefined; + let latestMouseX: number | undefined; + function timelineMouseMove(event: MouseEvent) { - if (requestAnimationTooltip) return; + latestMouseX = event.clientX; - requestAnimationTooltip = requestAnimationFrame(() => { - if (!playerSliderElement) return; - const rect = playerSliderElement.getBoundingClientRect(); - - const percent = Math.min( - Math.max((event.clientX - rect.left) / playerSliderElement.clientWidth, 0), - 1 - ); - - playerTimelineTimeHover = percent * (data.video.lengthSeconds ?? 0); - - document.documentElement.style.setProperty('--timeline-tooltip-left', `${percent * 100}%`); - - setPlayerTimelineChapters(playerTimelineTimeHover); + if (!requestAnimationTooltip) { + requestAnimationTooltip = requestAnimationFrame(updateTooltip); + } + } + function updateTooltip() { + if (!playerSliderElement || latestMouseX === undefined) { requestAnimationTooltip = undefined; - }); + return; + } + + const rect = playerSliderElement.getBoundingClientRect(); + const percent = Math.min(Math.max((latestMouseX - rect.left) / rect.width, 0), 1); + + playerTimelineTimeHover = percent * (data.video.lengthSeconds ?? 0); + setPlayerTimelineChapters(playerTimelineTimeHover); + + if (playerTimelineTooltip) { + const tooltipWidth = playerTimelineTooltip.offsetWidth; + const tooltipHeight = playerTimelineTooltip.offsetHeight; + const sliderWidth = playerSliderElement.clientWidth; + + let left = percent * sliderWidth; + left = Math.min(Math.max(left, tooltipWidth / 2), sliderWidth - tooltipWidth / 2); + playerTimelineTooltip.style.transform = `translateX(${left - tooltipWidth / 2}px)`; + + playerTimelineTooltip.style.top = `${-tooltipHeight - 5}px`; + } + + requestAnimationTooltip = undefined; } function onVideoClick( @@ -1302,31 +1318,26 @@ onmousemove={timelineMouseMove} bind:this={playerSliderElement} > + {#snippet timelineTooltip()} + {#if playerCloestSponsor} + {sponsorSegments[playerCloestSponsor.category]} +
+ {:else if playerCloestTimestamp} + {playerCloestTimestamp.title} +
+ {/if} + {/snippet}
{#if !userManualSeeking} -
- {#if playerCloestSponsor} - {sponsorSegments[playerCloestSponsor.category]} -
- {:else if playerCloestTimestamp} - {playerCloestTimestamp.title} -
- {/if} - +
+ {@render timelineTooltip()} {videoLength(playerTimelineTimeHover)}
{/if}
- {#if playerCloestSponsor} - {sponsorSegments[playerCloestSponsor.category]} -
- {:else if playerCloestTimestamp} - {playerCloestTimestamp.title} -
- {/if} - + {@render timelineTooltip()} {videoLength(currentTime)}
@@ -1808,4 +1819,13 @@ .hide-cursor { cursor: none; } + + .timeline.tooltip { + position: absolute; + left: 0; + transform: translateX(0%); + transition: none; + pointer-events: none; + will-change: transform; + } diff --git a/materialious/src/lib/css/global.css b/materialious/src/lib/css/global.css index 7cefd1cd..249f1ccc 100644 --- a/materialious/src/lib/css/global.css +++ b/materialious/src/lib/css/global.css @@ -121,6 +121,14 @@ menu { margin: 0 !important; } +.tooltip { + background-color: var(--surface) !important; + border: 0.1px solid var(--primary) !important; + box-shadow: none !important; + border-radius: 0.25rem !important; + color: var(--on-surface) !important; +} + @media screen and (max-width: 1000px) { menu.mobile { position: fixed !important;