use %, extract out to function
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import {useEffect, useId, useRef, useState} from 'react'
|
||||
import {useCallback, useEffect, useId, useRef, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyEmbedVideo} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
@@ -37,7 +37,7 @@ export function VideoEmbedInnerWeb({
|
||||
throw error
|
||||
}
|
||||
|
||||
const {hlsRef, loop, cueLineRef} = useHLS({
|
||||
const {hlsRef, loop, updateCuePositions} = useHLS({
|
||||
playlist: embed.playlist,
|
||||
setHasSubtitleTrack,
|
||||
setError,
|
||||
@@ -90,7 +90,7 @@ export function VideoEmbedInnerWeb({
|
||||
hasSubtitleTrack={hasSubtitleTrack}
|
||||
isGif={embed.presentation === 'gif'}
|
||||
altText={embed.alt}
|
||||
cueLineRef={cueLineRef}
|
||||
updateCuePositions={updateCuePositions}
|
||||
/>
|
||||
</div>
|
||||
</View>
|
||||
@@ -146,7 +146,47 @@ function useHLS({
|
||||
}, [Hls, setHlsLoading])
|
||||
|
||||
const hlsRef = useRef<HlsTypes.default | undefined>(undefined)
|
||||
const cueLineRef = useRef<number>(-2)
|
||||
const controlsVisibleRef = useRef(false)
|
||||
|
||||
/**
|
||||
* Repositions VTT subtitle cues using percentage-based line values
|
||||
* (snapToLines=false) so that multi-line/wrapped cues grow upward
|
||||
* instead of extending offscreen. Moves cues higher when controls
|
||||
* are visible to avoid occlusion by the scrub bar.
|
||||
*
|
||||
* Called from two sites:
|
||||
* - SUBTITLE_FRAG_PROCESSED: applies positioning to newly loaded cues
|
||||
* - VideoControls effect: updates positioning when controls show/hide
|
||||
*/
|
||||
const updateCuePositions = useCallback(
|
||||
(controlsVisible?: boolean) => {
|
||||
if (controlsVisible != null) {
|
||||
// save controlsVisible state so that when it's called from SUBTITLE_FRAG_PROCESSED,
|
||||
// the most recent value is used (as we won't know the control state there)
|
||||
controlsVisibleRef.current = controlsVisible
|
||||
}
|
||||
// magic numbers: cue position, % from top of video
|
||||
const line = controlsVisibleRef.current ? 70 : 85
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
for (let i = 0; i < video.textTracks.length; i++) {
|
||||
const track = video.textTracks[i]
|
||||
if (track.cues) {
|
||||
for (let j = 0; j < track.cues.length; j++) {
|
||||
const cue = track.cues[j] as VTTCue
|
||||
cue.snapToLines = false
|
||||
cue.line = line
|
||||
}
|
||||
}
|
||||
// toggle track mode to force the browser to re-render active cues
|
||||
if (track.mode === 'showing') {
|
||||
track.mode = 'hidden'
|
||||
track.mode = 'showing'
|
||||
}
|
||||
}
|
||||
},
|
||||
[videoRef],
|
||||
)
|
||||
const [lowQualityFragments, setLowQualityFragments] = useState<
|
||||
HlsTypes.Fragment[]
|
||||
>([])
|
||||
@@ -222,12 +262,8 @@ function useHLS({
|
||||
}
|
||||
})
|
||||
|
||||
hls.on(Hls.Events.CUES_PARSED, (_event, data) => {
|
||||
if (Array.isArray(data.cues)) {
|
||||
for (const cue of data.cues) {
|
||||
cue.line = cueLineRef.current
|
||||
}
|
||||
}
|
||||
hls.on(Hls.Events.SUBTITLE_FRAG_PROCESSED, () => {
|
||||
updateCuePositions()
|
||||
})
|
||||
|
||||
hls.on(Hls.Events.FRAG_BUFFERED, (_event, {frag}) => {
|
||||
@@ -317,6 +353,6 @@ function useHLS({
|
||||
return {
|
||||
hlsRef,
|
||||
loop: !hasLowQualityFragmentAtStart,
|
||||
cueLineRef,
|
||||
updateCuePositions,
|
||||
}
|
||||
}
|
||||
|
||||
+6
-20
@@ -48,7 +48,7 @@ export function Controls({
|
||||
hasSubtitleTrack,
|
||||
isGif,
|
||||
altText,
|
||||
cueLineRef,
|
||||
updateCuePositions,
|
||||
}: {
|
||||
videoRef: React.RefObject<HTMLVideoElement | null>
|
||||
hlsRef: React.RefObject<Hls | undefined | null>
|
||||
@@ -62,7 +62,7 @@ export function Controls({
|
||||
hasSubtitleTrack: boolean
|
||||
isGif: boolean
|
||||
altText?: string
|
||||
cueLineRef: React.RefObject<number>
|
||||
updateCuePositions: (controlsVisible?: boolean) => void
|
||||
}) {
|
||||
const {
|
||||
play,
|
||||
@@ -297,25 +297,11 @@ export function Controls({
|
||||
(interactingViaKeypress ? hasFocus : hovered)
|
||||
|
||||
// adjust subtitle cue positioning to avoid occlusion by controls
|
||||
const cueLine = showControls ? -4 : -2
|
||||
// uses percentage-based positioning (snapToLines=false) so wrapped
|
||||
// multi-line cues grow upward instead of extending offscreen
|
||||
useEffect(() => {
|
||||
cueLineRef.current = cueLine
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
for (let i = 0; i < video.textTracks.length; i++) {
|
||||
const track = video.textTracks[i]
|
||||
if (track.cues) {
|
||||
for (let j = 0; j < track.cues.length; j++) {
|
||||
;(track.cues[j] as VTTCue).line = cueLine
|
||||
}
|
||||
}
|
||||
// toggle track mode to force the browser to re-render active cues
|
||||
if (track.mode === 'showing') {
|
||||
track.mode = 'hidden'
|
||||
track.mode = 'showing'
|
||||
}
|
||||
}
|
||||
}, [cueLine, videoRef, cueLineRef])
|
||||
updateCuePositions(showControls)
|
||||
}, [showControls, updateCuePositions])
|
||||
|
||||
if (isGif) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user