videojs / video.js

Video.js - open source HTML5 video player
https://videojs.com
Other
38.16k stars 7.46k forks source link

Force time tooltips on mobile #8834

Open Daxxxis opened 3 months ago

Daxxxis commented 3 months ago

Description

I would like to force time tooltips to be displayed on mobile. As far as I know they are disabled there. Is there a reliable way to restore them? I also use the videojs-sprite-thumbnails plugin and I would like the thumbnails to be visible there.

Reduced test case

N/A

Steps to reproduce

Time tolltips are visible on desktop but not on mobile

Errors

No response

What version of Video.js are you using?

8.7.13

Video.js plugins used.

videojs-sprite-thumbnails

What browser(s) including version(s) does this occur with?

Chrome 128

What OS(es) and version(s) does this occur with?

Android

welcome[bot] commented 3 months ago

👋 Thanks for opening your first issue here! 👋

If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can. To help make it easier for us to investigate your issue, please follow the contributing guidelines.

mister-ben commented 3 months ago

Try player.controlBar.progressControl.seekBar.playProgressBar.addChild('TimeTooltip'). I believe there was an issue with some mobiles some years back that led to this being disabled, but that may not be relevant now.

phloxic commented 3 months ago

And similarly: player.controlBar.progressControl.mouseTimeDisplay.addChild('TimeTooltip')

Or via config:

videojs('myplayer', {
  controls: true,
  controlBar: {
    progressControl: {
      seekBar: {
        playProgressBar: {
          timeTooltip: true
        },
        mouseTimeDisplay: {
          timeTooltip: true
        }
      }
    }
  },
  [... more configuration goes here ]
});

Demo

Daxxxis commented 3 months ago

And similarly: player.controlBar.progressControl.mouseTimeDisplay.addChild('TimeTooltip')

Or via config:

videojs('myplayer', {
  controls: true,
  controlBar: {
    progressControl: {
      seekBar: {
        playProgressBar: {
          timeTooltip: true
        },
        mouseTimeDisplay: {
          timeTooltip: true
        }
      }
    }
  },
  [... more configuration goes here ]
});

Demo

This actually answers my question. There is only one problem with enableSmoothSeeking set to true, namely the tooltip does not follow the drag and remains at the starting point

phloxic commented 3 months ago

This actually answers my question. There is only one problem with enableSmoothSeeking set to true, namely the tooltip does not follow the drag and remains at the starting point

Indeed, it looks like smoothSeeking has no effect in this scenario.

amtins commented 3 months ago

@Daxxxis This doesn't work with smoothSeeking because of how events are handled. A simple solution would be:

I'd like to mention that this solution may not be perfect - I only spent 10 minutes on it. However, it does offer another approach.

      // The specificity was increased to unset the rules, maybe there is a better approach
      // This requires a little more work to ensure that the basic behavior of the desktop remains unchanged.
      .video-js.vjs-has-started .vjs-time-tooltip {
        visibility: unset;
      }
      .video-js.vjs-has-started .vjs-progress-control .vjs-mouse-display {
        display: unset;
      }
      // This Replace only what is needed
      const vjsProgressControl = videojs.getComponent("ProgressControl");

      class ProgressControl extends vjsProgressControl {
        /**
         * Creates an instance of this class.
         *
         * @param {Player} player
         *        The `Player` that this class should be attached to.
         *
         * @param {Object} [options]
         *        The key/value store of player options.
         */
        constructor(player, options) {
          super(player, options);
          this.handleMouseMove = videojs.fn.throttle(
            videojs.fn.bind_(this, super.handleMouseMove),
            videojs.fn.UPDATE_REFRESH_INTERVAL
          );
          this.throttledHandleMouseSeek = videojs.fn.throttle(
            videojs.fn.bind_(this, this.handleMouseSeek),
            videojs.fn.UPDATE_REFRESH_INTERVAL
          );
          this.handleMouseUpHandler_ = (e) => this.handleMouseUp(e);
          this.handleMouseDownHandler_ = (e) => this.handleMouseDown(e);

          this.enable();
        }

        /**
         * Disable all controls on the progress control and its children
         */
        disable() {
          this.children().forEach((child) => child.disable && child.disable());

          if (!this.enabled()) {
            return;
          }

          this.off(["pointerdown"], this.handleMouseDownHandler_);
          this.off(this.el_, "pointermove", this.handleMouseMove);

          this.removeListenersAddedOnMousedownAndTouchstart();

          this.addClass("disabled");

          this.enabled_ = false;

          // Restore normal playback state if controls are disabled while scrubbing
          if (this.player_.scrubbing()) {
            const seekBar = this.getChild("seekBar");

            this.player_.scrubbing(false);

            if (seekBar.videoWasPlaying) {
              silencePromise(this.player_.play());
            }
          }
        }

        /**
         * Enable all controls on the progress control and its children
         */
        enable() {
          this.children().forEach((child) => child.enable && child.enable());

          if (this.enabled()) {
            return;
          }

          this.on(["pointerdown"], this.handleMouseDownHandler_);
          this.on(this.el_, "pointermove", this.handleMouseMove);
          this.removeClass("disabled");

          this.enabled_ = true;
        }

        /**
         * Cleanup listeners after the user finishes interacting with the progress controls
         */
        removeListenersAddedOnMousedownAndTouchstart() {
          const doc = this.el_.ownerDocument;

          this.off(doc, "pointermove", this.throttledHandleMouseSeek);
          this.off(doc, "pointerup", this.handleMouseUpHandler_);
        }

        /**
         * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
         *
         * @param {Event} event
         *        `mousedown` or `touchstart` event that triggered this function
         *
         * @listens mousedown
         * @listens touchstart
         */
        handleMouseDown(event) {
          const doc = this.el_.ownerDocument;
          const seekBar = this.getChild("seekBar");

          if (seekBar) {
            seekBar.handleMouseDown(event);
          }

          this.on(doc, "pointermove", this.throttledHandleMouseSeek);
          this.on(doc, "pointerup", this.handleMouseUpHandler_);
        }
      }

      videojs.registerComponent("ProgressControl", ProgressControl);

      // Add the mouseTimeDisplay back
      if (videojs.browser.IS_IOS || videojs.browser.IS_ANDROID) {
        videojs.getComponent('SeekBar').prototype.options_.children.push('mouseTimeDisplay');
      }

      // Initialize
      const myPlayer = videojs('my-player');
phloxic commented 3 months ago

Thanks @amtins, I'll play around with that a bit when I find time.

phloxic commented 3 months ago

I integrated @amtins' impressive example code here, and it changes the behaviour. However, the problem is that you cannot really hover on a touch device. On touchstart you actually start to seek, and what happens is that the mouse time tooltip stays underneath the play tooltip. Which is certainly consistent, but kind of duplicates the behaviour of smooth seeking.

Certainly from the point of view of preview image display I do not see any particular gain for the viewer. - As far as I can see YouTube doesn't offer preview images in a tooltip on touch devices, but a pullup seek with a bar containing a sliding image sequence.

Daxxxis commented 3 months ago

As far as I can see YouTube doesn't offer preview images in a tooltip on touch devices, but a pullup seek with a bar containing a sliding image sequence.

Google actually recently got rid of thumbnail tooltips in favor of displaying thumbnails in place of the video. Previously they used something like this, which is what we're trying to do here.

Pulling up is actually called precise seeking and allows for more precise video seeking as name suggest (and it's also available on desktop) https://www.makeuseof.com/how-to-use-youtube-precise-seeking/

phloxic commented 3 months ago

@Daxxxis - yes. In desktop browsers, 'thumbnail seeking' (in combination with thumbnail previews by hovering) is still available as far as I can see. As an aside: I find the term 'precise seeking' somewhat misleading: it's more precise only if the thumbnail interval is shorter than the i-frame interval of the video, otherwise you get a more precise preview by waiting for the actual frame to load at a certain playhead position.

phloxic commented 3 months ago

@amtins - from a practical/user point of view I think the problem can be boiled down to this: