From 16d9329faf97f541eaa284eb1e42dd6a4049909e Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Sun, 27 Sep 2020 10:09:34 +0200 Subject: [PATCH] Fix errors, improve seeking --- src/routes/player/PreviewViewer.tsx | 5 ++--- src/routes/player/SeekBar.tsx | 13 +++++++++--- src/util/media/useImage.ts | 32 ++++++++++++++++++++--------- src/util/media/usePosition.ts | 4 ++-- src/util/media/useTextTrackCues.ts | 8 ++++++-- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/routes/player/PreviewViewer.tsx b/src/routes/player/PreviewViewer.tsx index fdca963..3ba9829 100644 --- a/src/routes/player/PreviewViewer.tsx +++ b/src/routes/player/PreviewViewer.tsx @@ -1,7 +1,7 @@ import React, {useMemo} from "react"; +import {useImage} from "../../util/media/useImage"; import {useTextTrackCues} from "../../util/media/useTextTrackCues"; import {parseImageSprite} from "../../util/sprite/parseImageSprite"; -import {useImage} from "../../util/media/useImage"; import {useImageSprite} from "../../util/sprite/useImageSprite"; interface Props { @@ -14,10 +14,9 @@ export function PreviewViewer({previewTrack, position}: Props) { const activeCue = cues.find(it => it.startTime <= position && it.endTime >= position) const activeUrl = activeCue ? new URL(activeCue.text, previewTrack.src).toString() : null; const imageSprite = useMemo(() => parseImageSprite(activeUrl), [activeUrl]); - console.log("active", activeUrl, imageSprite); const image = useImage(imageSprite?.src || null); const sprite = useImageSprite(imageSprite, image); return useMemo(() => ( - <img src={sprite || undefined}/> + <img alt="" src={sprite || undefined}/> ), [sprite]); } diff --git a/src/routes/player/SeekBar.tsx b/src/routes/player/SeekBar.tsx index 792c0ff..3a38e70 100644 --- a/src/routes/player/SeekBar.tsx +++ b/src/routes/player/SeekBar.tsx @@ -13,9 +13,10 @@ interface MousePosition { relative: number } -function getMousePosition(event: React.MouseEvent<HTMLDivElement>): MousePosition { +function getMousePosition(event: React.MouseEvent<HTMLDivElement>): MousePosition | null { const position = event.clientX - event.currentTarget.offsetLeft; const width = event.currentTarget.offsetWidth; + if (position > width) return null; return { absolute: position, relative: position / width @@ -35,12 +36,18 @@ export function SeekBar({video, previewTrack, duration}: Props) { onMouseLeave={() => setSeekPosition(null)} onClick={(event) => { const position = getMousePosition(event); + if (position === null) { + return; + } if (video) { video.currentTime = position.relative * duration; } }} onMouseMove={(event) => { const position = getMousePosition(event); + if (position === null) { + return; + } if (seekHeadRef.current) { seekHeadRef.current.style.transform = `translate3d(${position.absolute}px, 0, 0)` } @@ -57,7 +64,7 @@ export function SeekBar({video, previewTrack, duration}: Props) { }} /> </div> - ), [classes.seekBar, classes.seekHead, duration, isVisible]); + ), [classes.seekBar, classes.seekHead, duration, isVisible, video]); return ( <div> @@ -80,7 +87,7 @@ const useStyles = createUseStyles({ position: "absolute", top: 0, bottom: 0, - left: 0, + left: "-0.05rem", width: "0.1rem", background: "#f00", } diff --git a/src/util/media/useImage.ts b/src/util/media/useImage.ts index 7ec2d7e..4ac4010 100644 --- a/src/util/media/useImage.ts +++ b/src/util/media/useImage.ts @@ -1,18 +1,30 @@ -import {useEffect, useRef, useState} from "react"; +import {useEffect, useMemo, useState} from "react"; export const useImage = (url: string | null): HTMLImageElement | null => { - const image = useRef(new Image()) - image.current.setAttribute("crossorigin", "anonymous"); const [imageData, setImageData] = useState<HTMLImageElement | null>(null); - useEffect(() => { - try { - image.current.src = url || ""; - image.current.decode().then(() => { - setImageData(image.current); - }) - } catch (_) { + const image = useMemo(() => { + if (url === null) { + return null; + } else { + const image = new Image(); + image.setAttribute("crossorigin", "anonymous"); + image.src = url; + return image; } }, [url]); + useEffect(() => { + let isCancelled = false; + image?.decode().then(() => { + if (!isCancelled) { + setImageData(image); + } + }).catch(err => { + console.error("Error while decoding image " + url + ": ", err); + }); + return () => { + isCancelled = true; + } + }, [image, url]); return imageData; } diff --git a/src/util/media/usePosition.ts b/src/util/media/usePosition.ts index 770ea75..fb1b390 100644 --- a/src/util/media/usePosition.ts +++ b/src/util/media/usePosition.ts @@ -9,9 +9,9 @@ export const usePosition = (video: HTMLVideoElement | null) => { setPosition(video.currentTime); }) }; - video.addEventListener("progress", listener) + video.addEventListener("timeupdate", listener) return () => { - video.removeEventListener("progress", listener) + video.removeEventListener("timeupdate", listener) } } }, [video]); diff --git a/src/util/media/useTextTrackCues.ts b/src/util/media/useTextTrackCues.ts index 136cab6..1309d10 100644 --- a/src/util/media/useTextTrackCues.ts +++ b/src/util/media/useTextTrackCues.ts @@ -7,14 +7,18 @@ export const useTextTrackCues = (track: HTMLTrackElement) => { if (track.readyState >= 1) { setCues(Array.from(track.track.cues || [])) } else { + let animationFrame: number | null = null; const listener = () => { - window.requestAnimationFrame(() => { + animationFrame = window.requestAnimationFrame(() => { setCues(Array.from(track.track.cues || [])) }) }; track.addEventListener("load", listener) return () => { - track.removeEventListener("load", listener) + track.removeEventListener("load", listener); + if (animationFrame) { + window.cancelAnimationFrame(animationFrame); + } } } }, [track]); -- GitLab