Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit77c99d2

Browse files
authored
feat: Improve SmartTV scrubbing behavior (#8988)
1 parent5b9795d commit77c99d2

File tree

7 files changed

+309
-22
lines changed

7 files changed

+309
-22
lines changed

‎src/js/control-bar/progress-control/play-progress-bar.js‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,25 @@ class PlayProgressBar extends Component {
5656
*@param {number} seekBarPoint
5757
* A number from 0 to 1, representing a horizontal reference point
5858
* from the left edge of the {@link SeekBar}
59+
*
60+
*@param {Event} [event]
61+
* The `timeupdate` event that caused this function to run.
5962
*/
60-
update(seekBarRect,seekBarPoint){
63+
update(seekBarRect,seekBarPoint,event){
6164
consttimeTooltip=this.getChild('timeTooltip');
6265

6366
if(!timeTooltip){
6467
return;
6568
}
6669

67-
consttime=(this.player_.scrubbing()) ?
68-
this.player_.getCache().currentTime :
69-
this.player_.currentTime();
70+
// Combined logic: if an event with a valid pendingSeekTime getter exists, use it.
71+
consttime=(event&&
72+
event.target&&
73+
typeofevent.target.pendingSeekTime==='function') ?
74+
event.target.pendingSeekTime() :
75+
(this.player_.scrubbing() ?
76+
this.player_.getCache().currentTime :
77+
this.player_.currentTime());
7078

7179
timeTooltip.updateTime(seekBarRect,seekBarPoint,time);
7280
}

‎src/js/control-bar/progress-control/seek-bar.js‎

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,18 @@ class SeekBar extends Slider {
4444
// Avoid mutating the prototype's `children` array by creating a copy
4545
options.children=[...options.children];
4646

47-
constshouldDisableSeekWhileScrubbingOnMobile=player.options_.disableSeekWhileScrubbingOnMobile&&(IS_IOS||IS_ANDROID);
47+
constshouldDisableSeekWhileScrubbing=
48+
(player.options_.disableSeekWhileScrubbingOnMobile&&(IS_IOS||IS_ANDROID))||
49+
(player.options_.disableSeekWhileScrubbingOnSTV);
4850

4951
// Add the TimeTooltip as a child if we are on desktop, or on mobile with `disableSeekWhileScrubbingOnMobile: true`
50-
if((!IS_IOS&&!IS_ANDROID)||shouldDisableSeekWhileScrubbingOnMobile){
52+
if((!IS_IOS&&!IS_ANDROID)||shouldDisableSeekWhileScrubbing){
5153
options.children.splice(1,0,'mouseTimeDisplay');
5254
}
5355

5456
super(player,options);
5557

56-
this.shouldDisableSeekWhileScrubbingOnMobile_=shouldDisableSeekWhileScrubbingOnMobile;
58+
this.shouldDisableSeekWhileScrubbing_=shouldDisableSeekWhileScrubbing;
5759
this.pendingSeekTime_=null;
5860

5961
this.setEventHandlers_();
@@ -196,7 +198,7 @@ class SeekBar extends Slider {
196198

197199
// update the progress bar time tooltip with the current time
198200
if(this.bar){
199-
this.bar.update(Dom.getBoundingClientRect(this.el()),this.getProgress());
201+
this.bar.update(Dom.getBoundingClientRect(this.el()),this.getProgress(),event);
200202
}
201203
});
202204

@@ -233,6 +235,26 @@ class SeekBar extends Slider {
233235
this.player_.currentTime();
234236
}
235237

238+
/**
239+
* Getter and setter for pendingSeekTime.
240+
* Ensures the value is clamped between 0 and duration.
241+
*
242+
*@param {number|null} [time] - Optional. The new pending seek time, can be a number or null.
243+
*@return {number|null} - The current pending seek time.
244+
*/
245+
pendingSeekTime(time){
246+
if(time!==undefined){
247+
if(time!==null){
248+
constduration=this.player_.duration();
249+
250+
this.pendingSeekTime_=Math.max(0,Math.min(time,duration));
251+
}else{
252+
this.pendingSeekTime_=null;
253+
}
254+
}
255+
returnthis.pendingSeekTime_;
256+
}
257+
236258
/**
237259
* Get the percentage of media played so far.
238260
*
@@ -242,8 +264,8 @@ class SeekBar extends Slider {
242264
getPercent(){
243265
// If we have a pending seek time, we are scrubbing on mobile and should set the slider percent
244266
// to reflect the current scrub location.
245-
if(this.pendingSeekTime_){
246-
returnthis.pendingSeekTime_/this.player_.duration();
267+
if(this.pendingSeekTime()!==null){
268+
returnthis.pendingSeekTime()/this.player_.duration();
247269
}
248270

249271
constcurrentTime=this.getCurrentTime_();
@@ -284,7 +306,7 @@ class SeekBar extends Slider {
284306

285307
// Don't pause if we are on mobile and `disableSeekWhileScrubbingOnMobile: true`.
286308
// In that case, playback should continue while the player scrubs to a new location.
287-
if(!this.shouldDisableSeekWhileScrubbingOnMobile_){
309+
if(!this.shouldDisableSeekWhileScrubbing_){
288310
this.player_.pause();
289311
}
290312

@@ -351,8 +373,8 @@ class SeekBar extends Slider {
351373
}
352374

353375
// if on mobile and `disableSeekWhileScrubbingOnMobile: true`, keep track of the desired seek point but we won't initiate the seek until 'touchend'
354-
if(this.shouldDisableSeekWhileScrubbingOnMobile_){
355-
this.pendingSeekTime_=newTime;
376+
if(this.shouldDisableSeekWhileScrubbing_){
377+
this.pendingSeekTime(newTime);
356378
}else{
357379
this.userSeek_(newTime);
358380
}
@@ -402,10 +424,10 @@ class SeekBar extends Slider {
402424
this.player_.scrubbing(false);
403425

404426
// If we have a pending seek time, then we have finished scrubbing on mobile and should initiate a seek.
405-
if(this.pendingSeekTime_){
406-
this.userSeek_(this.pendingSeekTime_);
427+
if(this.pendingSeekTime()!==null){
428+
this.userSeek_(this.pendingSeekTime());
407429

408-
this.pendingSeekTime_=null;
430+
this.pendingSeekTime(null);
409431
}
410432

411433
/**
@@ -425,18 +447,46 @@ class SeekBar extends Slider {
425447
}
426448
}
427449

450+
/**
451+
* Handles pending seek time when `disableSeekWhileScrubbingOnSTV` is enabled.
452+
*
453+
*@param {number} stepAmount - The number of seconds to step (positive for forward, negative for backward).
454+
*/
455+
handlePendingSeek_(stepAmount){
456+
if(!this.player_.paused()){
457+
this.player_.pause();
458+
}
459+
460+
constcurrentPos=this.pendingSeekTime()!==null ?
461+
this.pendingSeekTime() :
462+
this.player_.currentTime();
463+
464+
this.pendingSeekTime(currentPos+stepAmount);
465+
this.player_.trigger({type:'timeupdate',target:this,manuallyTriggered:true});
466+
}
467+
428468
/**
429469
* Move more quickly fast forward for keyboard-only users
430470
*/
431471
stepForward(){
432-
this.userSeek_(this.player_.currentTime()+this.options().stepSeconds);
472+
// if `disableSeekWhileScrubbingOnSTV: true`, keep track of the desired seek point but we won't initiate the seek
473+
if(this.shouldDisableSeekWhileScrubbing_){
474+
this.handlePendingSeek_(this.options().stepSeconds);
475+
}else{
476+
this.userSeek_(this.player_.currentTime()+this.options().stepSeconds);
477+
}
433478
}
434479

435480
/**
436481
* Move more quickly rewind for keyboard-only users
437482
*/
438483
stepBack(){
439-
this.userSeek_(this.player_.currentTime()-this.options().stepSeconds);
484+
// if `disableSeekWhileScrubbingOnSTV: true`, keep track of the desired seek point but we won't initiate the seek
485+
if(this.shouldDisableSeekWhileScrubbing_){
486+
this.handlePendingSeek_(-this.options().stepSeconds);
487+
}else{
488+
this.userSeek_(this.player_.currentTime()-this.options().stepSeconds);
489+
}
440490
}
441491

442492
/**
@@ -448,6 +498,10 @@ class SeekBar extends Slider {
448498
*
449499
*/
450500
handleAction(event){
501+
if(this.pendingSeekTime()!==null){
502+
this.userSeek_(this.pendingSeekTime());
503+
this.pendingSeekTime(null);
504+
}
451505
if(this.player_.paused()){
452506
this.player_.play();
453507
}else{

‎src/js/control-bar/time-controls/current-time-display.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class CurrentTimeDisplay extends TimeDisplay {
3535

3636
if(this.player_.ended()){
3737
time=this.player_.duration();
38+
}elseif(event&&event.target&&typeofevent.target.pendingSeekTime==='function'){
39+
time=event.target.pendingSeekTime();
3840
}else{
3941
time=(this.player_.scrubbing()) ?this.player_.getCache().currentTime :this.player_.currentTime();
4042
}

‎src/js/player.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5577,7 +5577,8 @@ Player.prototype.options_ = {
55775577
},
55785578
// Default smooth seeking to false
55795579
enableSmoothSeeking:false,
5580-
disableSeekWhileScrubbingOnMobile:false
5580+
disableSeekWhileScrubbingOnMobile:false,
5581+
disableSeekWhileScrubbingOnSTV:false
55815582
};
55825583

55835584
TECH_EVENTS_RETRIGGER.forEach(function(event){

‎src/js/slider/slider.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,10 @@ class Slider extends Component {
325325
event.stopPropagation();
326326
this.stepForward();
327327
}else{
328+
if(this.pendingSeekTime()){
329+
this.pendingSeekTime(null);
330+
this.userSeek_(this.player_.currentTime());
331+
}
328332
super.handleKeyDown(event);
329333
}
330334

‎test/unit/controls.test.js‎

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,11 @@ QUnit.test('Seek bar percent should represent scrub location if we are scrubbing
320320
constseekBar=player.controlBar.progressControl.seekBar;
321321

322322
player.duration(100);
323-
seekBar.pendingSeekTime_=20;
323+
seekBar.pendingSeekTime(20);
324324

325325
assert.equal(seekBar.getPercent(),0.2,'seek bar percent set correctly to pending seek time');
326326

327-
seekBar.pendingSeekTime_=50;
327+
seekBar.pendingSeekTime(50);
328328

329329
assert.equal(seekBar.getPercent(),0.5,'seek bar percent set correctly to next pending seek time');
330330
});
@@ -680,3 +680,114 @@ QUnit.test('Remaing time negative sign can be optional', function(assert) {
680680
rtd2.dispose();
681681
player.dispose();
682682
});
683+
684+
QUnit.module('SmartTV UI Updates (Progress Bar & Time Display)',function(hooks){
685+
letplayer;
686+
letseekBar;
687+
letcurrentTimeDisplay;
688+
689+
hooks.beforeEach(function(){
690+
player=TestHelpers.makePlayer({
691+
spatialNavigation:{enabled:true},
692+
disableSeekWhileScrubbingOnSTV:true,
693+
controlBar:{
694+
progressControl:{
695+
seekBar:{
696+
stepSeconds:5
697+
}
698+
}
699+
}
700+
});
701+
702+
seekBar=player.controlBar.progressControl.seekBar;
703+
currentTimeDisplay=player.controlBar.getChild('currentTimeDisplay');
704+
705+
player.duration(100);
706+
});
707+
708+
hooks.afterEach(function(){
709+
player.dispose();
710+
});
711+
712+
QUnit.test('Step forward updates seek bar progress and current-time display',function(assert){
713+
player.currentTime(40);
714+
seekBar.stepForward();
715+
716+
assert.equal(
717+
seekBar.pendingSeekTime(),
718+
45,
719+
'pendingSeekTime should be 45 (40 + 5) after stepForward'
720+
);
721+
722+
assert.equal(
723+
seekBar.getPercent(),
724+
0.45,
725+
'Seek bar progress should reflect 45% progress after stepForward'
726+
);
727+
728+
assert.equal(
729+
currentTimeDisplay.formattedTime_,
730+
'0:45',
731+
'Current-time-display should update to 45s after stepForward'
732+
);
733+
});
734+
735+
QUnit.test('Step back updates seek bar progress and current-time display',function(assert){
736+
player.currentTime(40);
737+
seekBar.stepBack();
738+
739+
assert.equal(
740+
seekBar.pendingSeekTime(),
741+
35,
742+
'pendingSeekTime should be 35 (40 - 5) after stepBack'
743+
);
744+
745+
assert.equal(
746+
seekBar.getPercent(),
747+
0.35,
748+
'Seek bar progress should reflect 35% progress after stepBack'
749+
);
750+
751+
assert.equal(
752+
currentTimeDisplay.formattedTime_,
753+
'0:35',
754+
'Current-time-display should update to 35s after stepBack'
755+
);
756+
});
757+
758+
QUnit.test('Pressing enter finalizes the seek and updates UI',function(assert){
759+
player.currentTime(40);
760+
seekBar.stepForward();
761+
762+
seekBar.handleAction();
763+
764+
assert.equal(
765+
seekBar.pendingSeekTime(),
766+
null,
767+
'pendingSeekTime should be reset to null after seeking'
768+
);
769+
770+
assert.equal(
771+
seekBar.getPercent(),
772+
0.45,
773+
'Seek bar progress should remain at 45% after seeking'
774+
);
775+
776+
assert.equal(
777+
currentTimeDisplay.formattedTime_,
778+
'0:45',
779+
'Current-time-display should remain at 45s after seeking'
780+
);
781+
});
782+
783+
QUnit.test('Resets pendingSeekTime when SmartTV focus moves away without confirmation',function(assert){
784+
constuserSeekSpy=sinon.spy(seekBar,'userSeek_');
785+
786+
seekBar.trigger({type:'keydown',key:'ArrowUp'});
787+
assert.ok(seekBar.pendingSeekTime()!==null,'pendingSeekTime should be set after ArrowUp keydown');
788+
seekBar.trigger({type:'keydown',key:'ArrowLeft'});
789+
assert.equal(seekBar.pendingSeekTime(),null,'pendingSeekTime should be reset when SeekBar loses focus');
790+
assert.ok(userSeekSpy.calledWith(player.currentTime()),'userSeek_ should be called with current player time');
791+
userSeekSpy.restore();
792+
});
793+
});

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp