左手单手调视频进度

模拟方向键原生行为(支持长按),左手即可完成进度调整

  1. // ==UserScript==
  2. // @name 左手单手调视频进度
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description 模拟方向键原生行为(支持长按),左手即可完成进度调整
  6. // @author Vz
  7. // @license MIT
  8. // @match *://*.bilibili.com/*
  9. // @match *://*.youtube.com/*
  10. // @match *://*.pan.baidu.com/pfile/video?*
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. // ===================== 配置区域 =====================
  18. const CONFIG = {
  19. keyMap: {
  20. 'x': 'ArrowLeft', // X键模拟左方向键
  21. 'c': 'ArrowRight' // C键模拟右方向键
  22. },
  23. pressConfig: {
  24. repeatDelay: 500, // 长按首次触发延迟(毫秒)
  25. repeatInterval: 100 // 长按重复间隔(毫秒)
  26. }
  27. };
  28.  
  29. // ==================== 核心逻辑 =====================
  30. const activeKeys = new Map();
  31. let isModifierPressed = false;
  32.  
  33. function createKeyboardEvent(type, key) {
  34. const isLeft = key === 'ArrowLeft';
  35. return new KeyboardEvent(type, {
  36. key: key,
  37. code: key,
  38. keyCode: isLeft ? 37 : 39,
  39. bubbles: true,
  40. cancelable: true,
  41. composed: true
  42. });
  43. }
  44.  
  45. function dispatchToVideo(event) {
  46. // 优先发送给焦点元素
  47. const target = document.activeElement.tagName === 'VIDEO'
  48. ? document.activeElement
  49. : document.querySelector('video, .bpx-player-video-wrap video');
  50.  
  51. if (target) {
  52. target.dispatchEvent(event);
  53. return true;
  54. }
  55. return false;
  56. }
  57.  
  58. function startKeyRepeat(key) {
  59. const nativeKey = CONFIG.keyMap[key];
  60. if (!nativeKey) return;
  61.  
  62. // 发送初始keydown
  63. const downEvent = createKeyboardEvent('keydown', nativeKey);
  64. if (!dispatchToVideo(downEvent)) return;
  65.  
  66. // 设置重复定时器
  67. const timer = setInterval(() => {
  68. const repeatEvent = createKeyboardEvent('keydown', nativeKey);
  69. dispatchToVideo(repeatEvent);
  70. }, CONFIG.pressConfig.repeatInterval);
  71.  
  72. activeKeys.set(key, {
  73. timer: timer,
  74. isPressed: true
  75. });
  76. }
  77.  
  78. function stopKeyRepeat(key) {
  79. const record = activeKeys.get(key);
  80. if (record) {
  81. clearInterval(record.timer);
  82. const upEvent = createKeyboardEvent('keyup', CONFIG.keyMap[key]);
  83. dispatchToVideo(upEvent);
  84. activeKeys.delete(key);
  85. }
  86. }
  87.  
  88. // ================== 事件监听器 ====================
  89. function handleKeyDown(e) {
  90. // 忽略组合键
  91. if (e.ctrlKey || e.altKey || e.metaKey) {
  92. isModifierPressed = true;
  93. return;
  94. }
  95.  
  96. const key = e.key.toLowerCase();
  97. if (!CONFIG.keyMap[key] || isModifierPressed) return;
  98.  
  99. // 排除输入元素
  100. const activeEl = document.activeElement;
  101. if (['INPUT','TEXTAREA','SELECT'].includes(activeEl.tagName) || activeEl.isContentEditable) {
  102. return;
  103. }
  104.  
  105. e.preventDefault();
  106. e.stopPropagation();
  107.  
  108. if (!activeKeys.has(key)) {
  109. // 立即触发首次按下
  110. const downEvent = createKeyboardEvent('keydown', CONFIG.keyMap[key]);
  111. if (dispatchToVideo(downEvent)) {
  112. // 设置长按定时器
  113. const timer = setTimeout(() => {
  114. startKeyRepeat(key);
  115. }, CONFIG.pressConfig.repeatDelay);
  116.  
  117. activeKeys.set(key, {
  118. timer: timer,
  119. isPressed: true
  120. });
  121. }
  122. }
  123. }
  124.  
  125. function handleKeyUp(e) {
  126. const key = e.key.toLowerCase();
  127. if (activeKeys.has(key)) {
  128. clearTimeout(activeKeys.get(key).timer);
  129. stopKeyRepeat(key);
  130. }
  131. isModifierPressed = false;
  132. }
  133.  
  134. // ================== 初始化 ====================
  135. document.addEventListener('keydown', handleKeyDown, true);
  136. document.addEventListener('keyup', handleKeyUp, true);
  137.  
  138. // 保护系统快捷键
  139. document.addEventListener('keydown', (e) => {
  140. if (e.ctrlKey || e.metaKey) {
  141. // 清理所有激活状态
  142. activeKeys.forEach((_, key) => stopKeyRepeat(key));
  143. activeKeys.clear();
  144. }
  145. }, true);
  146. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址