Video Player: Fix isSeeking change (#5977)

This commit is contained in:
Alexander Zinchuk 2025-06-04 20:42:23 +02:00
parent ad4e496446
commit 850daef3b0
6 changed files with 71 additions and 35 deletions

View File

@ -13,6 +13,7 @@ import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
import buildClassName from '../../util/buildClassName';
import { captureEvents } from '../../util/captureEvents';
import { formatMediaDuration } from '../../util/dates/dateFormat';
import getPointerPosition from '../../util/events/getPointerPosition';
import { clamp, round } from '../../util/math';
import { useThrottledSignal } from '../../hooks/useAsyncResolvers';
@ -151,7 +152,7 @@ const SeekLine: FC<OwnProps> = ({
let offset = 0;
const getPreviewProps = (e: MouseEvent | TouchEvent) => {
const pageX = e instanceof MouseEvent ? e.pageX : e.touches[0].pageX;
const pageX = getPointerPosition(e).x;
const t = clamp(duration * ((pageX - seekerSize.left) / seekerSize.width), 0, duration);
if (isPreviewDisabled) return [t, 0];
if (!seekerSize.width) seekerSize = seeker.getBoundingClientRect();

View File

@ -1,13 +1,14 @@
import type { FC } from '../../lib/teact/teact';
import type React from '../../lib/teact/teact';
import {
memo, useEffect, useRef, useState,
memo, useEffect, useRef, useSignal, useState,
} from '../../lib/teact/teact';
import { getActions } from '../../global';
import type { ApiDimensions } from '../../api/types';
import { IS_IOS, IS_TOUCH_ENV, IS_YA_BROWSER } from '../../util/browser/windowEnvironment';
import getPointerPosition from '../../util/events/getPointerPosition';
import { clamp } from '../../util/math';
import safePlay from '../../util/safePlay';
import stopEvent from '../../util/stopEvent';
@ -112,17 +113,47 @@ const VideoPlayer: FC<OwnProps> = ({
] = usePictureInPicture(videoRef, handleEnterFullscreen, handleLeaveFullscreen);
const [, toggleControls, lockControls] = useControlsSignal();
const [getIsSeeking, setIsSeeking] = useSignal(false);
const lastMousePosition = useRef({ x: 0, y: 0 });
useEffect(() => {
const updateMousePosition = (e: MouseEvent | TouchEvent) => {
lastMousePosition.current = getPointerPosition(e);
};
window.addEventListener('mousemove', updateMousePosition);
window.addEventListener('touchmove', updateMousePosition);
return () => {
window.removeEventListener('mousemove', updateMousePosition);
window.removeEventListener('touchmove', updateMousePosition);
};
}, []);
const checkMousePositionAndToggleControls = useLastCallback((clientX: number, clientY: number) => {
const bounds = videoRef.current?.getBoundingClientRect();
if (!bounds) return;
if (clientX <= bounds.left || clientX >= bounds.right
|| clientY <= bounds.top || clientY >= bounds.bottom) {
if (!getIsSeeking()) {
toggleControls(false);
}
}
});
const handleVideoMove = useLastCallback(() => {
toggleControls(true);
});
const handleVideoLeave = useLastCallback((e) => {
const bounds = videoRef.current?.getBoundingClientRect();
if (!bounds) return;
if (e.clientX <= bounds.left || e.clientX >= bounds.right
|| e.clientY <= bounds.top || e.clientY >= bounds.bottom) {
toggleControls(false);
checkMousePositionAndToggleControls(e.clientX, e.clientY);
});
const handleSeekingChange = useLastCallback((isSeeking: boolean) => {
setIsSeeking(isSeeking);
if (!isSeeking) {
const { x, y } = lastMousePosition.current;
checkMousePositionAndToggleControls(x, y);
}
});
@ -374,6 +405,7 @@ const VideoPlayer: FC<OwnProps> = ({
onVolumeClick={handleVolumeMuted}
onVolumeChange={handleVolumeChange}
onPlaybackRateChange={handlePlaybackRateChange}
onSeekingChange={handleSeekingChange}
/>
)}
</div>

View File

@ -58,6 +58,7 @@ type OwnProps = {
onPlaybackRateChange: (playbackRate: number) => void;
onPlayPause: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
onSeek: (position: number) => void;
onSeekingChange: (isSeeking: boolean) => void;
};
const stopEvent = (e: React.MouseEvent<HTMLElement>) => {
@ -98,6 +99,7 @@ const VideoPlayerControls: FC<OwnProps> = ({
onPictureInPictureChange,
onPlayPause,
onSeek,
onSeekingChange,
}) => {
const [isPlaybackMenuOpen, openPlaybackMenu, closePlaybackMenu] = useFlag();
const [getCurrentTime] = useCurrentTimeSignal();
@ -146,10 +148,12 @@ const VideoPlayerControls: FC<OwnProps> = ({
const handleSeek = useLastCallback((position: number) => {
setIsSeeking(false);
onSeek(position);
onSeekingChange(false);
});
const handleSeekStart = useLastCallback(() => {
setIsSeeking(true);
onSeekingChange(true);
});
const volumeIcon: IconName = useMemo(() => {

View File

@ -1,11 +1,11 @@
import type { FC } from '../../lib/teact/teact';
import type React from '../../lib/teact/teact';
import {
memo, useCallback, useEffect, useMemo, useRef, useState,
} from '../../lib/teact/teact';
import buildClassName from '../../util/buildClassName';
import buildStyle from '../../util/buildStyle';
import getPointerPosition from '../../util/events/getPointerPosition';
import useOldLang from '../../hooks/useOldLang';
@ -59,7 +59,7 @@ const Draggable: FC<OwnProps> = ({
const handleMouseDown = (e: React.MouseEvent | React.TouchEvent) => {
e.stopPropagation();
e.preventDefault();
const { x, y } = getClientCoordinate(e);
const { x, y } = getPointerPosition(e);
setState({
...state,
@ -71,7 +71,7 @@ const Draggable: FC<OwnProps> = ({
};
const handleMouseMove = useCallback((e: MouseEvent | TouchEvent) => {
const { x, y } = getClientCoordinate(e);
const { x, y } = getPointerPosition(e);
const translation = {
x: x - state.origin.x,
@ -172,18 +172,3 @@ const Draggable: FC<OwnProps> = ({
};
export default memo(Draggable);
function getClientCoordinate(e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent) {
let x;
let y;
if ('touches' in e) {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
} else {
x = e.clientX;
y = e.clientY;
}
return { x, y };
}

View File

@ -9,6 +9,7 @@ import type { Point, Size } from '../types';
import { RESIZE_HANDLE_SELECTOR } from '../config';
import buildStyle from '../util/buildStyle';
import { captureEvents } from '../util/captureEvents';
import getPointerPosition from '../util/events/getPointerPosition';
import useFlag from './useFlag';
import useLastCallback from './useLastCallback';
@ -124,11 +125,11 @@ export default function useDraggable(
if (targetElement.closest('.no-drag') || !element) {
return;
}
const { pageX, pageY } = ('touches' in event) ? event.touches[0] : event;
const { x, y } = getPointerPosition(event);
const { left, top } = element.getBoundingClientRect();
setElementPositionOnStartTransform({ x: left, y: top });
setTransformStartPoint({ x: pageX, y: pageY });
setTransformStartPoint({ x, y });
startDragging();
});
@ -159,14 +160,14 @@ export default function useDraggable(
if (resizeHandle === undefined) return;
setHitResizeHandle(resizeHandle);
const { pageX, pageY } = ('touches' in event) ? event.touches[0] : event;
const { x, y } = getPointerPosition(event);
const {
left, right, top, bottom,
} = element.getBoundingClientRect();
setElementPositionOnStartTransform({ x: left, y: top });
setElementSizeOnStartTransform({ width: right - left, height: bottom - top });
setTransformStartPoint({ x: pageX, y: pageY });
setTransformStartPoint({ x, y });
startResizing();
});
@ -269,10 +270,10 @@ export default function useDraggable(
const handleDrag = useLastCallback((event: MouseEvent | TouchEvent) => {
if (!isDragging || !element) return;
const { pageX, pageY } = ('touches' in event) ? event.touches[0] : event;
const { x, y } = getPointerPosition(event);
const offsetX = pageX - transformStartPoint.x;
const offsetY = pageY - transformStartPoint.y;
const offsetX = x - transformStartPoint.x;
const offsetY = y - transformStartPoint.y;
const newX = elementPositionOnStartTransform.x + offsetX;
const newY = elementPositionOnStartTransform.y + offsetY;
@ -282,11 +283,11 @@ export default function useDraggable(
const handleResize = useLastCallback((event: MouseEvent | TouchEvent) => {
if (!isResizing || !element || hitResizeHandle === undefined) return;
const { pageX, pageY } = ('touches' in event) ? event.touches[0] : event;
const { x, y } = getPointerPosition(event);
const sizeOnStartTransform = getElementSizeOnStartTransform();
const pageVisibleX = Math.min(Math.max(0, pageX), getVisibleArea().width);
const pageVisibleY = Math.min(Math.max(0, pageY), getVisibleArea().height);
const pageVisibleX = Math.min(Math.max(0, x), getVisibleArea().width);
const pageVisibleY = Math.min(Math.max(0, y), getVisibleArea().height);
const offsetX = pageVisibleX - transformStartPoint.x;
const offsetY = pageVisibleY - transformStartPoint.y;

View File

@ -0,0 +1,13 @@
export default function getPointerPosition(e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent) {
if ('touches' in e) {
return {
x: e.touches[0].clientX,
y: e.touches[0].clientY,
};
}
return {
x: e.clientX,
y: e.clientY,
};
}