mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
fix(core): optimistic current time update on seek to prevent slider snap-back (#799)
This commit is contained in:
@@ -151,6 +151,51 @@ describe('timeFeature', () => {
|
||||
const result2 = await seek2Promise;
|
||||
expect(result2).toBe(20);
|
||||
});
|
||||
|
||||
it('optimistically updates currentTime before seeked event fires', () => {
|
||||
const video = createMockVideo({ readyState: HTMLMediaElement.HAVE_METADATA });
|
||||
const store = createStore<PlayerTarget>()(timeFeature);
|
||||
store.attach({ media: video, container: null });
|
||||
|
||||
expect(store.state.currentTime).toBe(0);
|
||||
|
||||
// Start seek but don't fire any DOM events.
|
||||
store.seek(45);
|
||||
|
||||
// Store should reflect target time immediately (no waiting for seeked).
|
||||
expect(store.state.currentTime).toBe(45);
|
||||
});
|
||||
|
||||
it('optimistically sets seeking to true before seeking event fires', () => {
|
||||
const video = createMockVideo({ readyState: HTMLMediaElement.HAVE_METADATA });
|
||||
const store = createStore<PlayerTarget>()(timeFeature);
|
||||
store.attach({ media: video, container: null });
|
||||
|
||||
expect(store.state.seeking).toBe(false);
|
||||
|
||||
store.seek(45);
|
||||
|
||||
expect(store.state.seeking).toBe(true);
|
||||
});
|
||||
|
||||
it('optimistic seeking is corrected by seeked event', async () => {
|
||||
const video = createMockVideo({ readyState: HTMLMediaElement.HAVE_METADATA });
|
||||
const store = createStore<PlayerTarget>()(timeFeature);
|
||||
store.attach({ media: video, container: null });
|
||||
|
||||
const resultPromise = store.seek(45);
|
||||
|
||||
expect(store.state.seeking).toBe(true);
|
||||
|
||||
// Simulate browser completing seek.
|
||||
Object.defineProperty(video, 'seeking', { value: false, configurable: true });
|
||||
video.dispatchEvent(new Event('seeked'));
|
||||
|
||||
await resultPromise;
|
||||
|
||||
expect(store.state.seeking).toBe(false);
|
||||
expect(store.state.currentTime).toBe(45);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ import { signalKeys } from '../signal-keys';
|
||||
|
||||
export const timeFeature = definePlayerFeature({
|
||||
name: 'time',
|
||||
state: ({ target, signals }): MediaTimeState => ({
|
||||
state: ({ target, signals, set }): MediaTimeState => ({
|
||||
currentTime: 0,
|
||||
duration: 0,
|
||||
seeking: false,
|
||||
@@ -23,6 +23,12 @@ export const timeFeature = definePlayerFeature({
|
||||
|
||||
// Perform the seek and wait for it to complete.
|
||||
const clampedTime = Math.max(0, Math.min(time, media.duration || Infinity));
|
||||
|
||||
// Optimistic update: reflect the target position immediately so UI consumers
|
||||
// (e.g. time slider) don't snap back to the old currentTime while waiting
|
||||
// for the browser's async seeking/seeked events.
|
||||
set({ currentTime: clampedTime, seeking: true });
|
||||
|
||||
media.currentTime = clampedTime;
|
||||
await onEvent(media, 'seeked', { signal }).catch(noop);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user