Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force time tooltips on mobile #8834

Open
Daxxxis opened this issue Aug 19, 2024 · 11 comments
Open

Force time tooltips on mobile #8834

Daxxxis opened this issue Aug 19, 2024 · 11 comments
Labels
needs: triage This issue needs to be reviewed

Comments

@Daxxxis
Copy link
Contributor

Daxxxis commented Aug 19, 2024

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

@Daxxxis Daxxxis added the needs: triage This issue needs to be reviewed label Aug 19, 2024
Copy link

welcome bot commented Aug 19, 2024

👋 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
Copy link
Contributor

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
Copy link
Contributor

phloxic commented Aug 21, 2024

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
Copy link
Contributor Author

Daxxxis commented Aug 21, 2024

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
Copy link
Contributor

phloxic commented Aug 21, 2024

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
Copy link
Contributor

amtins commented Aug 21, 2024

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

  • modify the css so that the tooltip appears
  • replace event handling with pointer events, see the pointer events compatibility table.
  • add the mouseTimeDisplay to the SeekBar option to get around
    if (!IS_IOS && !IS_ANDROID) {
    SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
    }
    .

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
Copy link
Contributor

phloxic commented Aug 22, 2024

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

@phloxic
Copy link
Contributor

phloxic commented Aug 22, 2024

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
Copy link
Contributor Author

Daxxxis commented Aug 22, 2024

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
Copy link
Contributor

phloxic commented Aug 23, 2024

@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
Copy link
Contributor

phloxic commented Aug 25, 2024

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

  • the play progress time tooltip works ok
  • the mouse time tooltip works on an actual mousemove
  • but once a seek has happened the mouse time tooltip shows up on its previous position (most of the time) when one just taps the seek bar (while showing the correct time) or taps the screen to make the controlbar appear
  • the tooltips appear also in desktop browsers when hovering over/clicking the screen - this is most likely due to the CSS unset. Only adding the 2 rules for mobile devices could take care of that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs: triage This issue needs to be reviewed
Projects
None yet
Development

No branches or pull requests

4 participants