From c96f0a8f63fb1419a76dec11978a01c00968d257 Mon Sep 17 00:00:00 2001 From: PikachuEXE Date: Wed, 29 Jan 2025 10:14:49 +0800 Subject: [PATCH 1/2] ! Fix scroll to seek/change volume/playback rate changing too fast with touchpad --- .../ft-shaka-video-player.js | 74 ++++++++++++------- src/renderer/helpers/utils.js | 25 +++++++ 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js index 1d4f3115f4bdf..767bd75434f33 100644 --- a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js +++ b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js @@ -22,7 +22,8 @@ import { import { addKeyboardShortcutToActionTitle, showToast, - writeFileWithPicker + writeFileWithPicker, + throttle, } from '../../helpers/utils' /** @typedef {import('../../helpers/sponsorblock').SponsorBlockCategory} SponsorBlockCategory */ @@ -944,13 +945,13 @@ export default defineComponent({ if (event.ctrlKey || event.metaKey) { if (videoPlaybackRateMouseScroll.value) { - mouseScrollPlaybackRate(event) + mouseScrollPlaybackRateHandler(event) } } else { if (videoVolumeMouseScroll.value) { - mouseScrollVolume(event) + mouseScrollVolumeHandler(event) } else if (videoSkipMouseScroll.value) { - mouseScrollSkip(event) + mouseScrollSkipHandler(event) } } } @@ -988,7 +989,7 @@ export default defineComponent({ } // make scrolling over volume slider change the volume - container.value.querySelector('.shaka-volume-bar').addEventListener('wheel', mouseScrollVolume) + container.value.querySelector('.shaka-volume-bar').addEventListener('wheel', mouseScrollVolumeHandler) // title overlay when the video is fullscreened // placing this inside the controls container so that we can fade it in and out at the same time as the controls @@ -1970,56 +1971,79 @@ export default defineComponent({ // #region mouse scroll handlers + const mouseScrollThrottleWaitMs = 100 + /** * @param {WheelEvent} event */ function mouseScrollPlaybackRate(event) { - event.preventDefault() - if ((event.deltaY < 0 || event.deltaX > 0)) { changePlayBackRate(0.05) } else if ((event.deltaY > 0 || event.deltaX < 0)) { changePlayBackRate(-0.05) } } + const mouseScrollPlaybackRateThrottle = throttle(mouseScrollPlaybackRate, mouseScrollThrottleWaitMs) + /** + * @param {WheelEvent} event + */ + function mouseScrollPlaybackRateHandler(event) { + event.preventDefault() + + mouseScrollPlaybackRateThrottle(event) + } /** * @param {WheelEvent} event */ function mouseScrollSkip(event) { + if ((event.deltaY < 0 || event.deltaX > 0)) { + seekBySeconds(defaultSkipInterval.value * player.getPlaybackRate(), true) + } else if ((event.deltaY > 0 || event.deltaX < 0)) { + seekBySeconds(-defaultSkipInterval.value * player.getPlaybackRate(), true) + } + } + const mouseScrollSkipThrottle = throttle(mouseScrollSkip, mouseScrollThrottleWaitMs) + /** + * @param {WheelEvent} event + */ + function mouseScrollSkipHandler(event) { if (canSeek()) { event.preventDefault() + mouseScrollSkipThrottle(event) + } + } + + /** + * @param {WheelEvent} event + */ + function mouseScrollVolume(event) { + const video_ = video.value + + if (video_.muted && (event.deltaY < 0 || event.deltaX > 0)) { + video_.muted = false + video_.volume = 0 + } + + if (!video_.muted) { if ((event.deltaY < 0 || event.deltaX > 0)) { - seekBySeconds(defaultSkipInterval.value * player.getPlaybackRate(), true) + changeVolume(0.05) } else if ((event.deltaY > 0 || event.deltaX < 0)) { - seekBySeconds(-defaultSkipInterval.value * player.getPlaybackRate(), true) + changeVolume(-0.05) } } } - + const mouseScrollVolumeThrottle = throttle(mouseScrollVolume, mouseScrollThrottleWaitMs) /** * @param {WheelEvent} event */ - function mouseScrollVolume(event) { + function mouseScrollVolumeHandler(event) { if (!event.ctrlKey && !event.metaKey) { event.preventDefault() event.stopPropagation() - const video_ = video.value - - if (video_.muted && (event.deltaY < 0 || event.deltaX > 0)) { - video_.muted = false - video_.volume = 0 - } - - if (!video_.muted) { - if ((event.deltaY < 0 || event.deltaX > 0)) { - changeVolume(0.05) - } else if ((event.deltaY > 0 || event.deltaX < 0)) { - changeVolume(-0.05) - } - } + mouseScrollVolumeThrottle(event) } } diff --git a/src/renderer/helpers/utils.js b/src/renderer/helpers/utils.js index a60b13460ad00..37e6d91ce25c0 100644 --- a/src/renderer/helpers/utils.js +++ b/src/renderer/helpers/utils.js @@ -1013,3 +1013,28 @@ export function debounce(func, wait) { }, wait) } } + +/** + * @template {Function} T + * @param {T} func + * @param {number} wait + * @returns {T} + */ +export function throttle(func, wait) { + let isWaiting + + // Using a fully fledged function here instead of an arrow function + // so that we can get `this` and pass it onto the original function. + // Vue components using the options API use `this` alot. + return function (...args) { + const context = this + if (!isWaiting) { + func.apply(context, args) + + isWaiting = true + setTimeout(() => { + isWaiting = false + }, wait) + } + } +} From 63a6a31e61a8d340c65d2b68c3ce527cbbeb4737 Mon Sep 17 00:00:00 2001 From: PikachuEXE Date: Tue, 17 Jun 2025 08:58:18 +0800 Subject: [PATCH 2/2] * Only apply scroll throttle when delta value is small (assumed to be from touchpad), update throttle wait 100 > 200ms --- .../ft-shaka-video-player.js | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js index b96df852b39b3..c335a101576bc 100644 --- a/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js +++ b/src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js @@ -1961,7 +1961,7 @@ export default defineComponent({ // #region mouse scroll handlers - const mouseScrollThrottleWaitMs = 100 + const mouseScrollThrottleWaitMs = 200 /** * @param {WheelEvent} event @@ -1980,7 +1980,12 @@ export default defineComponent({ function mouseScrollPlaybackRateHandler(event) { event.preventDefault() - mouseScrollPlaybackRateThrottle(event) + // Touchpad scroll = small deltaX/deltaY + if (Math.abs(event.deltaX) <= 5 && Math.abs(event.deltaY) <= 5) { + mouseScrollPlaybackRateThrottle(event) + } else { + mouseScrollPlaybackRate(event) + } } /** @@ -2001,7 +2006,12 @@ export default defineComponent({ if (canSeek()) { event.preventDefault() - mouseScrollSkipThrottle(event) + // Touchpad scroll = small deltaX/deltaY + if (Math.abs(event.deltaX) <= 5 && Math.abs(event.deltaY) <= 5) { + mouseScrollSkipThrottle(event) + } else { + mouseScrollSkip(event) + } } } @@ -2033,7 +2043,12 @@ export default defineComponent({ event.preventDefault() event.stopPropagation() - mouseScrollVolumeThrottle(event) + // Touchpad scroll = small deltaX/deltaY + if (Math.abs(event.deltaX) <= 5 && Math.abs(event.deltaY) <= 5) { + mouseScrollVolumeThrottle(event) + } else { + mouseScrollVolume(event) + } } }