Skip to content

Commit

Permalink
feat: Seek bar smooth seeking (#8287)
Browse files Browse the repository at this point in the history
* refactor(player): decrease the indentation level in the currentTime method

* fix(player): cache_.currentTime is not updated when the current time is set

Updating cache_.currentTime as soon as the currentTime is set avoids having to wait for the timeupdate event, which results in:

- making cache_.currentTime more reliable
- updating the progress bar on mouse up after dragging when the media is paused.

See also: #6232, #6234, #6370, #6372

* feat: add an option to handle smooth seeking

Adds a player option called enableSmoothSeeking, which is false by default,
to provide a smoother seeking experience on mobile and desktop devices.

Usage:
```javascript
// Enables the smooth seeking
const player = videojs('player', {enableSmoothSeeking: true});

// Disable the smooth seeking
player.options({enableSmoothSeeking: false});
```

- **player.js** add an `option` called `enableSmoothSeeking`
- **time-display.js** add a listener to the `seeking` event if `enableSmoothSeeking` is `true` allowing to update the `CurrentTimeDisplay` and `RemainingTimeDisplay` in real time
- **seek-bar.js** `update` the seek bar on `mousemove` event  if `enableSmoothSeeking` is `true`
- add test cases
  • Loading branch information
amtins committed Jan 2, 2024
1 parent af0fca3 commit 608a585
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/js/control-bar/progress-control/seek-bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ class SeekBar extends Slider {

// Set new time (tell player to seek to new time)
this.userSeek_(newTime);

if (this.player_.options_.enableSmoothSeeking) {
this.update();
}
}

enable() {
Expand Down
16 changes: 15 additions & 1 deletion src/js/control-bar/time-controls/time-display.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class TimeDisplay extends Component {
constructor(player, options) {
super(player, options);

this.on(player, ['timeupdate', 'ended'], (e) => this.updateContent(e));
this.on(player, ['timeupdate', 'ended', 'seeking'], (e) => this.update(e));
this.updateTextNode_();
}

Expand Down Expand Up @@ -71,6 +71,20 @@ class TimeDisplay extends Component {
super.dispose();
}

/**
* Updates the displayed time according to the `updateContent` function which is defined in the child class.
*
* @param {Event} [event]
* The `timeupdate`, `ended` or `seeking` (if enableSmoothSeeking is true) event that caused this function to be called.
*/
update(event) {
if (!this.player_.options_.enableSmoothSeeking && event.type === 'seeking') {
return;
}

this.updateContent(event);
}

/**
* Updates the time display text node with a new time
*
Expand Down
4 changes: 3 additions & 1 deletion src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -5413,7 +5413,9 @@ Player.prototype.options_ = {
breakpoints: {},
responsive: false,
audioOnlyMode: false,
audioPosterMode: false
audioPosterMode: false,
// Default smooth seeking to false
enableSmoothSeeking: false
};

TECH_EVENTS_RETRIGGER.forEach(function(event) {
Expand Down
63 changes: 63 additions & 0 deletions test/unit/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3423,3 +3423,66 @@ QUnit.test('should not reset the error when the tech triggers an error that is n
errorStub.restore();
log.error.restore();
});

QUnit.test('smooth seeking set to false should not update the display time components or the seek bar', function(assert) {
const player = TestHelpers.makePlayer({});
const {
currentTimeDisplay,
remainingTimeDisplay,
progressControl: {
seekBar
}
} = player.controlBar;
const currentTimeDisplayUpdateContent = sinon.spy(currentTimeDisplay, 'updateContent');
const remainingTimeDisplayUpdateContent = sinon.spy(remainingTimeDisplay, 'updateContent');
const seekBarUpdate = sinon.spy(seekBar, 'update');

assert.false(player.options().enableSmoothSeeking, 'enableSmoothSeeking is false by default');

player.trigger('seeking');

assert.ok(currentTimeDisplayUpdateContent.notCalled, 'currentTimeDisplay updateContent was not called');
assert.ok(remainingTimeDisplayUpdateContent.notCalled, 'remainingTimeDisplay updateContent was not called');

seekBar.trigger('mousedown');
seekBar.trigger('mousemove');

assert.ok(seekBarUpdate.notCalled, 'seekBar update was not called');

currentTimeDisplayUpdateContent.restore();
remainingTimeDisplayUpdateContent.restore();
seekBarUpdate.restore();
player.dispose();
});

QUnit.test('smooth seeking set to true should update the display time components and the seek bar', function(assert) {
const player = TestHelpers.makePlayer({enableSmoothSeeking: true});
const {
currentTimeDisplay,
remainingTimeDisplay,
progressControl: {
seekBar
}
} = player.controlBar;
const currentTimeDisplayUpdateContent = sinon.spy(currentTimeDisplay, 'updateContent');
const remainingTimeDisplayUpdateContent = sinon.spy(remainingTimeDisplay, 'updateContent');
const seekBarUpdate = sinon.spy(seekBar, 'update');

assert.true(player.options().enableSmoothSeeking, 'enableSmoothSeeking is true');

player.duration(1);
player.trigger('seeking');

assert.ok(currentTimeDisplayUpdateContent.called, 'currentTimeDisplay updateContent was called');
assert.ok(remainingTimeDisplayUpdateContent.called, 'remainingTimeDisplay updateContent was called');

seekBar.trigger('mousedown');
seekBar.trigger('mousemove');

assert.ok(seekBarUpdate.called, 'seekBar update was called');

currentTimeDisplayUpdateContent.restore();
remainingTimeDisplayUpdateContent.restore();
seekBarUpdate.restore();
player.dispose();
});

0 comments on commit 608a585

Please sign in to comment.