[ui] VideoViewer has mouseover previews

feat/vaults
Tomáš Mládek 2022-04-09 21:36:40 +02:00
parent 3b82b0ee69
commit 4a8fd90f1f
No known key found for this signature in database
GPG Key ID: 65E225C8B3E2ED8A
4 changed files with 144 additions and 26 deletions

View File

@ -3,6 +3,7 @@
import Spinner from "../utils/Spinner.svelte";
import FragmentViewer from "./blobs/FragmentViewer.svelte";
import ModelViewer from "./blobs/ModelViewer.svelte";
import VideoViewer from "./blobs/VideoViewer.svelte";
import HashBadge from "./HashBadge.svelte";
export let address: string;
@ -48,6 +49,8 @@
/>
{:else if fragment}
<FragmentViewer {address} detail={false} />
{:else if video}
<VideoViewer {address} detail={false} />
{:else}
<div class="image" class:loaded={imageLoaded == address || !handled}>
{#if handled && imageLoaded != address}

View File

@ -6,6 +6,7 @@
import ImageViewer from "./blobs/ImageViewer.svelte";
import ModelViewer from "./blobs/ModelViewer.svelte";
import TextViewer from "./blobs/TextViewer.svelte";
import VideoViewer from "./blobs/VideoViewer.svelte";
import UpLink from "./UpLink.svelte";
export let address: string;
@ -50,24 +51,7 @@
<AudioViewer {address} {detail} {editable} />
{/if}
{#if video}
{#if imageLoaded != address}
<Spinner />
<img
src="api/thumb/{address}"
alt={address}
on:load={() => (imageLoaded = address)}
on:error={() => (imageLoaded = address)}
/>
{:else}
<!-- svelte-ignore a11y-media-has-caption -->
<video
controls
preload="auto"
src="api/raw/{address}"
poster="api/thumb/{address}"
/>
{/if}
<VideoViewer detail {address} />
{/if}
{#if image}
<ImageViewer {address} {editable} {detail} />
@ -116,7 +100,6 @@
}
}
video,
img,
.text {
width: 100%;
@ -136,8 +119,4 @@
img {
object-fit: contain;
}
video {
background: rgba(128, 128, 128, 128);
}
</style>

View File

@ -0,0 +1,135 @@
<script lang="ts">
import { debounce, throttle } from "lodash";
import Icon from "../../utils/Icon.svelte";
import Spinner from "../../utils/Spinner.svelte";
export let address: string;
export let detail: boolean;
enum State {
LOADING = "loading",
PREVIEW = "preview",
PREVIEWING = "previewing",
PLAYING = "playing",
ERRORED = "errored",
}
let state = State.PREVIEW;
let videoEl: HTMLVideoElement;
const seek = throttle((progress: number) => {
if (state === State.PREVIEWING) {
videoEl.currentTime = videoEl.duration * progress;
}
}, 100);
function updatePreviewPosition(ev: MouseEvent) {
if (state === State.PREVIEW || state === State.PREVIEWING) {
state = State.PREVIEWING;
const bcr = videoEl.getBoundingClientRect();
const progress = (ev.clientX - bcr.x) / bcr.width;
seek(progress);
}
}
function resetPreview() {
if (state === State.PREVIEWING) {
state = State.PREVIEW;
videoEl.load();
}
}
function startPlaying() {
state = State.PLAYING;
videoEl.play();
}
</script>
<div class="video-viewer {state}">
{#if state === State.LOADING}
<Spinner />
<img
src="api/thumb/{address}"
alt={address}
on:load={() => (state = State.PREVIEW)}
on:error={() => (state = State.ERRORED)}
/>
{:else}
<div class="player" style="--icon-size: {detail ? 100 : 32}px">
<!-- svelte-ignore a11y-media-has-caption -->
<video
preload={detail ? "auto" : "metadata"}
src="api/raw/{address}"
poster="api/thumb/{address}"
on:mousemove={updatePreviewPosition}
on:mouseleave={resetPreview}
on:click={startPlaying}
controls={state === State.PLAYING}
bind:this={videoEl}
/>
<div class="play-icon">
<div class="icon">
<Icon plain name="play" />
</div>
</div>
</div>
{/if}
</div>
<style lang="scss">
.video-viewer {
video {
width: 100%;
max-height: 100%;
background: rgba(128, 128, 128, 128);
transition: filter 0.2s;
}
.player {
position: relative;
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
$size: var(--icon-size);
width: $size;
height: $size;
color: white;
font-size: $size;
line-height: 1;
border-radius: calc($size / 4);
border: .07em solid white;
.icon {
margin-left: .04em;
}
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
&.preview {
video {
filter: brightness(0.75);
}
.play-icon {
opacity: 1;
}
}
&.previewing video {
cursor: pointer;
}
}
</style>

View File

@ -3,6 +3,7 @@
</script>
<script lang="ts">
export let plain = false;
export let name: string;
if (!loaded) {
@ -14,10 +15,10 @@
}
</script>
<i class="bx bx-{name}" />
<i class="bx bx-{name}" class:plain />
<style>
.bx {
.bx:not(.plain) {
font-size: 115%;
}
</style>
</style>