Skip to content

Commit 08f9b5e

Browse files
committed
! Fix scroll to seek/change volume/playback rate changing too fast with touchpad
1 parent fb098b3 commit 08f9b5e

File tree

2 files changed

+74
-25
lines changed

2 files changed

+74
-25
lines changed

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

Lines changed: 49 additions & 25 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 */
@@ -904,13 +905,13 @@ export default defineComponent({
904905

905906
if (event.ctrlKey || event.metaKey) {
906907
if (videoPlaybackRateMouseScroll.value) {
907-
mouseScrollPlaybackRate(event)
908+
mouseScrollPlaybackRateHandler(event)
908909
}
909910
} else {
910911
if (videoVolumeMouseScroll.value) {
911-
mouseScrollVolume(event)
912+
mouseScrollVolumeHandler(event)
912913
} else if (videoSkipMouseScroll.value) {
913-
mouseScrollSkip(event)
914+
mouseScrollSkipHandler(event)
914915
}
915916
}
916917
}
@@ -947,7 +948,7 @@ export default defineComponent({
947948
}
948949

949950
// make scrolling over volume slider change the volume
950-
container.value.querySelector('.shaka-volume-bar').addEventListener('wheel', mouseScrollVolume)
951+
container.value.querySelector('.shaka-volume-bar').addEventListener('wheel', mouseScrollVolumeHandler)
951952

952953
// title overlay when the video is fullscreened
953954
// placing this inside the controls container so that we can fade it in and out at the same time as the controls
@@ -1925,56 +1926,79 @@ export default defineComponent({
19251926

19261927
// #region mouse scroll handlers
19271928

1929+
const mouseScrollThrottleWaitMs = 100
1930+
19281931
/**
19291932
* @param {WheelEvent} event
19301933
*/
19311934
function mouseScrollPlaybackRate(event) {
1932-
event.preventDefault()
1933-
19341935
if ((event.deltaY < 0 || event.deltaX > 0)) {
19351936
changePlayBackRate(0.05)
19361937
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
19371938
changePlayBackRate(-0.05)
19381939
}
19391940
}
1941+
const mouseScrollPlaybackRateThrottle = throttle(mouseScrollPlaybackRate, mouseScrollThrottleWaitMs)
1942+
/**
1943+
* @param {WheelEvent} event
1944+
*/
1945+
function mouseScrollPlaybackRateHandler(event) {
1946+
event.preventDefault()
1947+
1948+
mouseScrollPlaybackRateThrottle(event)
1949+
}
19401950

19411951
/**
19421952
* @param {WheelEvent} event
19431953
*/
19441954
function mouseScrollSkip(event) {
1955+
if ((event.deltaY < 0 || event.deltaX > 0)) {
1956+
seekBySeconds(defaultSkipInterval.value * video.value.playbackRate, true)
1957+
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
1958+
seekBySeconds(-defaultSkipInterval.value * video.value.playbackRate, true)
1959+
}
1960+
}
1961+
const mouseScrollSkipThrottle = throttle(mouseScrollSkip, mouseScrollThrottleWaitMs)
1962+
/**
1963+
* @param {WheelEvent} event
1964+
*/
1965+
function mouseScrollSkipHandler(event) {
19451966
if (canSeek()) {
19461967
event.preventDefault()
19471968

1969+
mouseScrollSkipThrottle(event)
1970+
}
1971+
}
1972+
1973+
/**
1974+
* @param {WheelEvent} event
1975+
*/
1976+
function mouseScrollVolume(event) {
1977+
const video_ = video.value
1978+
1979+
if (video_.muted && (event.deltaY < 0 || event.deltaX > 0)) {
1980+
video_.muted = false
1981+
video_.volume = 0
1982+
}
1983+
1984+
if (!video_.muted) {
19481985
if ((event.deltaY < 0 || event.deltaX > 0)) {
1949-
seekBySeconds(defaultSkipInterval.value * video.value.playbackRate, true)
1986+
changeVolume(0.05)
19501987
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
1951-
seekBySeconds(-defaultSkipInterval.value * video.value.playbackRate, true)
1988+
changeVolume(-0.05)
19521989
}
19531990
}
19541991
}
1955-
1992+
const mouseScrollVolumeThrottle = throttle(mouseScrollVolume, mouseScrollThrottleWaitMs)
19561993
/**
19571994
* @param {WheelEvent} event
19581995
*/
1959-
function mouseScrollVolume(event) {
1996+
function mouseScrollVolumeHandler(event) {
19601997
if (!event.ctrlKey && !event.metaKey) {
19611998
event.preventDefault()
19621999
event.stopPropagation()
19632000

1964-
const video_ = video.value
1965-
1966-
if (video_.muted && (event.deltaY < 0 || event.deltaX > 0)) {
1967-
video_.muted = false
1968-
video_.volume = 0
1969-
}
1970-
1971-
if (!video_.muted) {
1972-
if ((event.deltaY < 0 || event.deltaX > 0)) {
1973-
changeVolume(0.05)
1974-
} else if ((event.deltaY > 0 || event.deltaX < 0)) {
1975-
changeVolume(-0.05)
1976-
}
1977-
}
2001+
mouseScrollVolumeThrottle(event)
19782002
}
19792003
}
19802004

src/renderer/helpers/utils.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,3 +1052,28 @@ export function debounce(func, wait) {
10521052
}, wait)
10531053
}
10541054
}
1055+
1056+
/**
1057+
* @template {Function} T
1058+
* @param {T} func
1059+
* @param {number} wait
1060+
* @returns {T}
1061+
*/
1062+
export function throttle(func, wait) {
1063+
let isWaiting
1064+
1065+
// Using a fully fledged function here instead of an arrow function
1066+
// so that we can get `this` and pass it onto the original function.
1067+
// Vue components using the options API use `this` alot.
1068+
return function (...args) {
1069+
const context = this
1070+
if (!isWaiting) {
1071+
func.apply(context, args)
1072+
1073+
isWaiting = true
1074+
setTimeout(() => {
1075+
isWaiting = false
1076+
}, wait)
1077+
}
1078+
}
1079+
}

0 commit comments

Comments
 (0)