Skip to content

Commit 56046b9

Browse files
authored
Fix scroll to seek/change volume/playback rate changing too fast with touchpad (#6666)
* ! Fix scroll to seek/change volume/playback rate changing too fast with touchpad * * Only apply scroll throttle when delta value is small (assumed to be from touchpad), update throttle wait 100 > 200ms
1 parent 00d4695 commit 56046b9

File tree

2 files changed

+88
-24
lines changed

2 files changed

+88
-24
lines changed

src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import {
2222
import {
2323
addKeyboardShortcutToActionTitle,
2424
showToast,
25-
writeFileWithPicker
25+
writeFileWithPicker,
26+
throttle,
2627
} from '../../helpers/utils'
2728

2829
/** @typedef {import('../../helpers/sponsorblock').SponsorBlockCategory} SponsorBlockCategory */
@@ -946,13 +947,13 @@ export default defineComponent({
946947

947948
if (event.ctrlKey || event.metaKey) {
948949
if (videoPlaybackRateMouseScroll.value) {
949-
mouseScrollPlaybackRate(event)
950+
mouseScrollPlaybackRateHandler(event)
950951
}
951952
} else {
952953
if (videoVolumeMouseScroll.value) {
953-
mouseScrollVolume(event)
954+
mouseScrollVolumeHandler(event)
954955
} else if (videoSkipMouseScroll.value) {
955-
mouseScrollSkip(event)
956+
mouseScrollSkipHandler(event)
956957
}
957958
}
958959
}
@@ -990,7 +991,7 @@ export default defineComponent({
990991
}
991992

992993
// make scrolling over volume slider change the volume
993-
container.value.querySelector('.shaka-volume-bar').addEventListener('wheel', mouseScrollVolume)
994+
container.value.querySelector('.shaka-volume-bar').addEventListener('wheel', mouseScrollVolumeHandler)
994995

995996
// title overlay when the video is fullscreened
996997
// placing this inside the controls container so that we can fade it in and out at the same time as the controls
@@ -1966,30 +1967,56 @@ export default defineComponent({
19661967

19671968
// #region mouse scroll handlers
19681969

1970+
const mouseScrollThrottleWaitMs = 200
1971+
19691972
/**
19701973
* @param {WheelEvent} event
19711974
*/
19721975
function mouseScrollPlaybackRate(event) {
1973-
event.preventDefault()
1974-
19751976
if ((event.deltaY < 0 || event.deltaX > 0)) {
19761977
changePlayBackRate(0.05)
19771978
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
19781979
changePlayBackRate(-0.05)
19791980
}
19801981
}
1982+
const mouseScrollPlaybackRateThrottle = throttle(mouseScrollPlaybackRate, mouseScrollThrottleWaitMs)
1983+
/**
1984+
* @param {WheelEvent} event
1985+
*/
1986+
function mouseScrollPlaybackRateHandler(event) {
1987+
event.preventDefault()
1988+
1989+
// Touchpad scroll = small deltaX/deltaY
1990+
if (Math.abs(event.deltaX) <= 5 && Math.abs(event.deltaY) <= 5) {
1991+
mouseScrollPlaybackRateThrottle(event)
1992+
} else {
1993+
mouseScrollPlaybackRate(event)
1994+
}
1995+
}
19811996

19821997
/**
19831998
* @param {WheelEvent} event
19841999
*/
19852000
function mouseScrollSkip(event) {
2001+
if ((event.deltaY < 0 || event.deltaX > 0)) {
2002+
seekBySeconds(defaultSkipInterval.value * player.getPlaybackRate(), true)
2003+
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
2004+
seekBySeconds(-defaultSkipInterval.value * player.getPlaybackRate(), true)
2005+
}
2006+
}
2007+
const mouseScrollSkipThrottle = throttle(mouseScrollSkip, mouseScrollThrottleWaitMs)
2008+
/**
2009+
* @param {WheelEvent} event
2010+
*/
2011+
function mouseScrollSkipHandler(event) {
19862012
if (canSeek()) {
19872013
event.preventDefault()
19882014

1989-
if ((event.deltaY < 0 || event.deltaX > 0)) {
1990-
seekBySeconds(defaultSkipInterval.value * player.getPlaybackRate(), true)
1991-
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
1992-
seekBySeconds(-defaultSkipInterval.value * player.getPlaybackRate(), true)
2015+
// Touchpad scroll = small deltaX/deltaY
2016+
if (Math.abs(event.deltaX) <= 5 && Math.abs(event.deltaY) <= 5) {
2017+
mouseScrollSkipThrottle(event)
2018+
} else {
2019+
mouseScrollSkip(event)
19932020
}
19942021
}
19952022
}
@@ -1998,23 +2025,35 @@ export default defineComponent({
19982025
* @param {WheelEvent} event
19992026
*/
20002027
function mouseScrollVolume(event) {
2001-
if (!event.ctrlKey && !event.metaKey) {
2002-
event.preventDefault()
2003-
event.stopPropagation()
2028+
const video_ = video.value
20042029

2005-
const video_ = video.value
2030+
if (video_.muted && (event.deltaY < 0 || event.deltaX > 0)) {
2031+
video_.muted = false
2032+
video_.volume = 0
2033+
}
20062034

2007-
if (video_.muted && (event.deltaY < 0 || event.deltaX > 0)) {
2008-
video_.muted = false
2009-
video_.volume = 0
2035+
if (!video_.muted) {
2036+
if ((event.deltaY < 0 || event.deltaX > 0)) {
2037+
changeVolume(0.05)
2038+
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
2039+
changeVolume(-0.05)
20102040
}
2041+
}
2042+
}
2043+
const mouseScrollVolumeThrottle = throttle(mouseScrollVolume, mouseScrollThrottleWaitMs)
2044+
/**
2045+
* @param {WheelEvent} event
2046+
*/
2047+
function mouseScrollVolumeHandler(event) {
2048+
if (!event.ctrlKey && !event.metaKey) {
2049+
event.preventDefault()
2050+
event.stopPropagation()
20112051

2012-
if (!video_.muted) {
2013-
if ((event.deltaY < 0 || event.deltaX > 0)) {
2014-
changeVolume(0.05)
2015-
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
2016-
changeVolume(-0.05)
2017-
}
2052+
// Touchpad scroll = small deltaX/deltaY
2053+
if (Math.abs(event.deltaX) <= 5 && Math.abs(event.deltaY) <= 5) {
2054+
mouseScrollVolumeThrottle(event)
2055+
} else {
2056+
mouseScrollVolume(event)
20182057
}
20192058
}
20202059
}

src/renderer/helpers/utils.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,3 +1003,28 @@ export function debounce(func, wait) {
10031003
}, wait)
10041004
}
10051005
}
1006+
1007+
/**
1008+
* @template {Function} T
1009+
* @param {T} func
1010+
* @param {number} wait
1011+
* @returns {T}
1012+
*/
1013+
export function throttle(func, wait) {
1014+
let isWaiting
1015+
1016+
// Using a fully fledged function here instead of an arrow function
1017+
// so that we can get `this` and pass it onto the original function.
1018+
// Vue components using the options API use `this` alot.
1019+
return function (...args) {
1020+
const context = this
1021+
if (!isWaiting) {
1022+
func.apply(context, args)
1023+
1024+
isWaiting = true
1025+
setTimeout(() => {
1026+
isWaiting = false
1027+
}, wait)
1028+
}
1029+
}
1030+
}

0 commit comments

Comments
 (0)