fix(core): prevent mobile controls flash on first tap after auto-hide#1556
Open
R-Delfino95 wants to merge 3 commits into
Open
fix(core): prevent mobile controls flash on first tap after auto-hide#1556R-Delfino95 wants to merge 3 commits into
R-Delfino95 wants to merge 3 commits into
Conversation
…ntrols On touch devices, pointermove between pointerdown and pointerup was calling setActive() and flipping controlsVisible to true. The gesture recognizer's 200 ms deferred tap then read the already-true value and toggled controls off — causing a visible flash on every first tap after auto-hide. Fix: touch pointermove only reschedules the idle timer when already active; it no longer forces visibility on. Also guard mouseleave against the synthetic event Android Chrome fires within 500 ms of touchend. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
👷 Deploy request for vjs10-site pending review.Visit the deploys page to approve it
|
|
@R-Delfino95 is attempting to deploy a commit to the Mux Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1446
Summary
On Android Chrome, after controls auto-hide, the first tap on the video area made the controls flash briefly and immediately disappear. The second tap behaved correctly. The bug reoccurred every cycle (interact with any button → wait for auto-hide → tap → flash).
Root cause
Three things interact:
The skin registers a
tapgesture withaction="toggleControls"(touch) alongsidedoubletapgestures (seekStep×2,toggleFullscreen). Because doubletap bindings exist,TapRecognizer.handleUpdefers the tap action byDOUBLETAP_WINDOW = 200 msto disambiguate, and the deferred callback re-reads live state at fire time.<media-container>'s ownpointeruplistener callsthis.focus({ preventScroll: true })so keyboard events reach the hotkey coordinator. That call firesfocusinsynchronously.The container's
pointeruplistener is registered inconnectedCallback, beforecontrolsFeature.attachregisters its own. So on everypointerup, the container's listener runs first.The result, on the first tap after auto-hide (
controlsVisible=false):pointerdown(touch) → recordspointerDownTime.pointerup(touch) fires. Container's listener runs first →this.focus()→focusinfires synchronously.focusinhandler incontrolsFeaturecallssetActive()→ flipscontrolsVisibletotrue.pointeruplistener runs next (records the touch timestamp), then the gesture coordinator defers the tap action by 200 ms.toggleControls(), readscontrolsVisible=true, callssetInactive()→ controls hide. That's the flash.The second tap worked because focus was already inside the container, so
this.focus()was skipped and no synchronousfocusinfired.A secondary issue: Android Chrome dispatches a synthetic
mouseleaveaftertouchend, which independently calledsetInactive().Fix
In
packages/core/src/dom/store/features/controls.ts:pointerdownas well aspointerup(renamedlastTouchUpAt→lastTouchAt). Recording onpointerdownis essential: the container'spointeruplistener runs before ours, so without an earlier timestamp the guard below would always seelastTouchAt=0and not short-circuit.focusinagainst synthetic focus within 500 ms of any touch event — mirrors the existingmouseleaveguard.pointermove, only refresh the idle timer when already active; don't flip visibility. Prevents the same race if apointermovefires mid-tap on devices that drift slightly during touch.mouseleaveagainst the synthetic event Android Chrome dispatches aftertouchend.Keyboard-driven focus still activates controls —
lastTouchAtis only set on touch events, so non-touchfocusin(e.g. tabbing into the container) is unaffected.Test plan
pnpm -F @videojs/core test src/dom/store/features/tests/controls.test.ts— 34 tests pass, including new coverage for:focusinfired between touchpointerdownandpointerup(the actual race) is ignored,focusinafter the guard window still activates, syntheticmouseleavewithin 500 ms of touchpointerupis ignored, touchpointermovedoes not flip visibility mid-gesture.pnpm typecheckpnpm -F @videojs/core buildpnpm lint:fix:fileon both touched filesfocusinguard only suppresses inside the 500 ms touch window).Note
Medium Risk
Changes core controls visibility heuristics for touch events (
pointermove,focusin,mouseleave) and adds timing-based guards, which could affect control activation/idle behavior across devices and input types.Overview
Prevents Android Chrome’s first-tap-after-idle controls flash by adding touch-aware guards in
controlsFeaturethat ignore syntheticfocusinandmouseleaveevents shortly after touch interactions.Updates
pointermovehandling so touch movement only refreshes the idle timer (without reactivating/showing controls mid-gesture), and recordslastTouchAton both touchpointerdownandpointerupto cover the focus race window. Adds targeted tests covering these touch/synthetic-event scenarios.Reviewed by Cursor Bugbot for commit e74dd65. Bugbot is set up for automated code reviews on this repo. Configure here.