Skip to content

Commit 56a92bb

Browse files
KimKyuHoiclaudeavelad
committed
fix(UI): sync seek position with hover timestamp using consistent position calculation (#9818)
## Problem Previously, click/drag in the seek bar relied on the browser's default range entry event. The hover preview used 'getValueFromPosition()' separately. This resulted in a mismatch between the hover timestamp and the actual search position, This problem was particularly noticeable in long videos. ## Fix When the mouse is down, 'e.Use preventDefault()` to block the browser's default calculation, 'setBarValueForMouse_()' which follows the same pattern as the existing 'setBarValueForTouch_()' Add a function to directly call 'getValueFromPosition()' when clicking and dragging I did, now hover, click, drag, touch all go through the same function, so timestamp The preview and navigation positions are always synchronized. In addition, the hard-coded 'thumbWide = 12' is 'thumbRadius = 6' ('range_elements.less') I modified it to the defined '@thumb-size') and the browser changed the thumb [[rect.left + thumbRadius, rect.right - thumbRadius]]' only within the range of '[rect.left + thumbRadius]', Reflected the offset value when the click position was reverse-calculated as a value. --------- Co-authored-by: Claude Code <noreply@anthropic.com> Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
1 parent 872f598 commit 56a92bb

File tree

1 file changed

+52
-20
lines changed

1 file changed

+52
-20
lines changed

ui/range_element.js

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
4848
/** @private {boolean} */
4949
this.isChanging_ = false;
5050

51+
/** @private {boolean} */
52+
this.isMouseChanging_ = false;
53+
5154
/** @protected {!HTMLInputElement} */
5255
this.bar =
5356
/** @type {!HTMLInputElement} */ (document.createElement('input'));
@@ -74,23 +77,48 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
7477
this.bar.disabled = false;
7578
});
7679

77-
this.eventManager.listen(this.controls, 'showingui', (e) => {
80+
this.eventManager.listen(this.controls, 'showingui', () => {
7881
this.showingUITimer_.tickAfter(/* seconds= */ 0);
7982
});
8083

81-
this.eventManager.listen(this.controls, 'hidingui', (e) => {
84+
this.eventManager.listen(this.controls, 'hidingui', () => {
8285
this.showingUITimer_.stop();
8386
this.bar.disabled = true;
8487
});
8588

8689
this.eventManager.listen(this.bar, 'mousedown', (e) => {
8790
if (!this.bar.disabled) {
91+
// Prevent native range update to use getValueFromPosition()
92+
// consistently with the hover preview.
93+
e.preventDefault();
94+
this.bar.focus();
8895
this.isChanging_ = true;
96+
this.isMouseChanging_ = true;
97+
this.setBarValueForMouse_(e);
8998
this.onChangeStart();
99+
this.onChange();
90100
e.stopPropagation();
91101
}
92102
});
93103

104+
this.eventManager.listen(document, 'mousemove', (e) => {
105+
if (this.isMouseChanging_) {
106+
this.setBarValueForMouse_(e);
107+
this.onChange();
108+
}
109+
});
110+
111+
this.eventManager.listen(document, 'mouseup', (e) => {
112+
if (this.isMouseChanging_) {
113+
this.isMouseChanging_ = false;
114+
if (this.isChanging_) {
115+
this.isChanging_ = false;
116+
this.setBarValueForMouse_(e);
117+
this.onChangeEnd();
118+
}
119+
}
120+
});
121+
94122
this.eventManager.listen(this.bar, 'touchstart', (e) => {
95123
if (!this.bar.disabled) {
96124
this.isChanging_ = true;
@@ -133,6 +161,8 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
133161
this.eventManager.listen(this.bar, 'mouseup', (e) => {
134162
if (this.isChanging_) {
135163
this.isChanging_ = false;
164+
this.isMouseChanging_ = false;
165+
this.setBarValueForMouse_(e);
136166
this.onChangeEnd();
137167
e.stopPropagation();
138168
}
@@ -141,6 +171,7 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
141171
this.eventManager.listen(this.bar, 'blur', () => {
142172
if (this.isChanging_) {
143173
this.isChanging_ = false;
174+
this.isMouseChanging_ = false;
144175
this.onChangeEnd();
145176
}
146177
});
@@ -250,39 +281,40 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
250281
* @return {number}
251282
*/
252283
getValueFromPosition(clientX) {
253-
// Get the bounding rectangle of the range input element
254284
const rect = this.bar.getBoundingClientRect();
255-
256-
// Parse the min, max, and step attributes from the input element
257285
const min = parseFloat(this.bar.min);
258286
const max = parseFloat(this.bar.max);
259287
const step = parseFloat(this.bar.step) || 1;
260288

261-
// Define the effective range of the thumb movement
262-
// 12 is the value of @thumb-size in range_elements.less. Note: for
263-
// everything to work, this value has to be synchronized.
264-
const thumbWidth = 12;
265-
const minX = rect.left + thumbWidth / 2;
266-
const maxX = rect.right - thumbWidth / 2;
267-
268-
// Clamp the touch X position to stay within the thumb's movement range
289+
// thumbRadius is half of @thumb-size, as defined in range_elements.less.
290+
// The browser renders the thumb only within the movement range
291+
// [rect.left + thumbRadius, rect.right - thumbRadius], so we must apply
292+
// the same offset when mapping a click position back to a value.
293+
// Note: thumbRadius must stay in sync with @thumb-size in
294+
// range_elements.less.
295+
const thumbRadius = 6; // half of @thumb-size in range_elements.less
296+
const minX = rect.left + thumbRadius;
297+
const maxX = rect.right - thumbRadius;
269298
const clampedX = Math.max(minX, Math.min(maxX, clientX));
270-
271-
// Calculate the percentage of the track that the clamped X represents
272299
const percent = (clampedX - minX) / (maxX - minX);
273300

274-
// Convert the percentage into a value within the input's range
275301
let value = min + percent * (max - min);
276-
277-
// Round the value to the nearest step
278302
value = Math.round((value - min) / step) * step + min;
279-
280-
// Ensure the value stays within the min and max bounds
281303
value = Math.min(max, Math.max(min, value));
282304

283305
return value;
284306
}
285307

308+
/**
309+
* Synchronize the mouse position with the range value.
310+
* @param {Event} event
311+
* @private
312+
*/
313+
setBarValueForMouse_(event) {
314+
this.bar.value = this.getValueFromPosition(
315+
/** @type {MouseEvent} */ (event).clientX);
316+
}
317+
286318
/**
287319
* Synchronize the touch position with the range value.
288320
* Comes in handy on iOS, where users have to grab the handle in order

0 commit comments

Comments
 (0)