小窗预览

拖拽链接时在弹出窗口中打开链接,并在打开前提供预览,使用 Edge 的预读技术。同时在小窗口打开时在背后添加亚克力效果。

当前为 2024-08-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Small Window Preview
  3. // @description Drag a link to open it in a popup window with a preview before opening, using Edge's prerendering technology. Also, add an acrylic effect behind the window when it's open.
  4. // @name:en Small Window Preview
  5. // @description:en Drag a link to open it in a popup window with a preview before opening, using Edge's prerendering technology. Also, add an acrylic effect behind the window when it's open.
  6. // @name:zh-CN 小窗预览
  7. // @description:zh-CN 拖拽链接时在弹出窗口中打开链接,并在打开前提供预览,使用 Edge 的预读技术。同时在小窗口打开时在背后添加亚克力效果。
  8. // @name:zh-TW 小窗預覽
  9. // @description:zh-TW 拖曳連結時在彈出視窗中打開連結,並在打開前提供預覽,使用 Edge 的預讀技術。同時在小窗口打開時在背後添加壓克力效果。
  10. // @name:ja 小窓プレビュー
  11. // @description:ja リンクをドラッグしてポップアップウィンドウでプレビューを表示し、Edge のプレビュー技術を使用して開く前にリンクを開きます。また、ウィンドウが開いているときにアクリル効果を背景に追加します。
  12. // @name:vi Xem trước cửa sổ nhỏ
  13. // @description:vi Kéo thả liên kết để mở nó trong một cửa sổ popup với chế độ xem trước trước khi mở, sử dụng công nghệ tiên đoán của Edge. Đồng thời, thêm hiệu ứng acrylic phía sau cửa sổ khi nó mở.
  14. // @version 2.4.0.13
  15. // @author 人民的勤务员 <toniaiwanowskiskr47@gmail.com> & hiisme
  16. // @match *://*/*
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_unregisterMenuCommand
  19. // @grant GM_getValue
  20. // @grant GM_setValue
  21. // @grant GM_info
  22. // @namespace https://gf.qytechs.cn/users/217852
  23. // @supportURL https://github.com/ChinaGodMan/UserScripts/issues
  24. // @homepageURL https://github.com/ChinaGodMan/UserScripts
  25. // @icon https://github.com/ChinaGodMan/UserScripts/raw/main/docs/icon/Scripts%20Icons/icons8-POPUPWINDOW-48.png
  26. // @license MIT
  27. // ==/UserScript==
  28.  
  29. const translate = (function () {
  30. const userLang = (navigator.languages && navigator.languages[0]) || navigator.language || 'en'
  31. const strings = {
  32. 'en': {
  33. actionMode: 'Select Trigger Mode',
  34. actionMode1: 'Long Press',
  35. actionMode2: 'Drag',
  36. actionMode0: 'Both',
  37. longPressDuration: 'Long Press Duration',
  38. blurEnabled: 'Toggle Blur Effect',
  39. blurIntensity: 'Set Blur Intensity',
  40. closeOnMouseClick: 'Toggle Close on Mouse Click',
  41. closeOnScroll: 'Toggle Close on Scroll',
  42. windowWidth: 'Set Window Width',
  43. windowHeight: 'Set Window Height',
  44. setLongPressDuration: 'Enter Long Press Duration (milliseconds):',
  45. setBlurIntensityprompt: 'Enter Blur Intensity (0-10):',
  46. toggleActionMode: 'Select Trigger Mode:\n1: Long Press\n2: Drag\n0: Both',
  47. setWindowSizeprompt: 'Enter Window Size (pixels):',
  48. showCountdown: 'Show countdown progress bar',
  49. saveWindowConfig: 'Record window position',
  50. showCountdowndrag: 'Show drag timeout progress bar',
  51. dragTimeOut: 'Drag timeout duration',
  52. },
  53. 'zh-CN': {
  54. actionMode: '选择触发方式',
  55. actionMode1: '长按',
  56. actionMode2: '拖拽',
  57. actionMode0: '两者都用',
  58. longPressDuration: '长按触发时间',
  59. blurEnabled: '模糊效果',
  60. blurIntensity: '设置模糊强度',
  61. closeOnMouseClick: '点击关闭小窗',
  62. closeOnScroll: '滚动关闭小窗',
  63. windowWidth: '设置小窗宽度',
  64. windowHeight: '设置小窗高度',
  65. setLongPressDuration: '输入长按触发时间(毫秒):',
  66. setBlurIntensityprompt: '输入模糊强度(0-10):',
  67. toggleActionMode: '选择触发方式:\n1: 长按\n2: 拖拽\n0: 两者都用',
  68. setWindowSizeprompt: '输入小窗口(像素):',
  69. showCountdown: '显示长按倒计时进度条',
  70. saveWindowConfig: '记录窗口位置',
  71. showCountdowndrag: '显示拖拽超时进度条',
  72. dragTimeOut: '拖拽超时时间',
  73. },
  74. 'zh-TW': {
  75. actionMode: '選擇觸發方式',
  76. actionMode1: '長按',
  77. actionMode2: '拖曳',
  78. actionMode0: '兩者都用',
  79. longPressDuration: '長按觸發時間',
  80. blurEnabled: '切換模糊效果',
  81. blurIntensity: '設定模糊強度',
  82. closeOnMouseClick: '切換點擊關閉小窗',
  83. closeOnScroll: '切換滾動關閉小窗',
  84. windowWidth: '設定小窗寬度',
  85. windowHeight: '設定小窗高度',
  86. setLongPressDuration: '輸入長按觸發時間(毫秒):',
  87. setBlurIntensityprompt: '輸入模糊強度(0-10):',
  88. toggleActionMode: '選擇觸發方式:\n1: 長按\n2: 拖曳\n0: 兩者都用',
  89. setWindowSizeprompt: '輸入小窗口(像素):',
  90. showCountdown: '顯示倒數計時進度條',
  91. saveWindowConfig: '記錄窗口位置',
  92. showCountdowndrag: '顯示拖曳逾時進度條',
  93. dragTimeOut: '拖曳逾時時間',
  94. },
  95. 'ja': {
  96. actionMode: 'トリガーモードの選択',
  97. actionMode1: '長押し',
  98. actionMode2: 'ドラッグ',
  99. actionMode0: '両方',
  100. longPressDuration: '長押しの時間',
  101. blurEnabled: 'ぼかし効果の切り替え',
  102. blurIntensity: 'ぼかしの強度を設定',
  103. closeOnMouseClick: 'マウスクリックで閉じる切り替え',
  104. closeOnScroll: 'スクロールで閉じる切り替え',
  105. windowWidth: 'ウィンドウ幅の設定',
  106. windowHeight: 'ウィンドウ高さの設定',
  107. setLongPressDuration: '長押しの時間(ミリ秒)を入力:',
  108. setBlurIntensityprompt: 'ぼかしの強度(0-10)を入力:',
  109. toggleActionMode: 'トリガーモードの選択:\n1: 長押し\n2: ドラッグ\n0: 両方',
  110. setWindowSizeprompt: 'ウィンドウサイズ(ピクセル)を入力:',
  111. showCountdown: 'カウントダウン進行状況を表示',
  112. saveWindowConfig: 'ウィンドウの位置を記録',
  113. showCountdowndrag: 'ドラッグタイムアウトの進行状況バーを表示',
  114. dragTimeOut: 'ドラッグタイムアウト時間',
  115. },
  116. 'vi': {
  117. actionMode: 'Chọn chế độ kích hoạt',
  118. actionMode1: 'Nhấn lâu',
  119. actionMode2: 'Kéo thả',
  120. actionMode0: 'Cả hai',
  121. longPressDuration: 'Thời gian nhấn lâu',
  122. blurEnabled: 'Bật hiệu ứng mờ',
  123. blurIntensity: 'Cài đặt độ mờ',
  124. closeOnMouseClick: 'Bật/tắt đóng cửa sổ bằng nhấp chuột',
  125. closeOnScroll: 'Bật/tắt đóng cửa sổ khi cuộn',
  126. windowWidth: 'Cài đặt chiều rộng cửa sổ',
  127. windowHeight: 'Cài đặt chiều cao cửa sổ',
  128. setLongPressDuration: 'Nhập thời gian nhấn lâu (mili giây):',
  129. setBlurIntensityprompt: 'Nhập độ mờ (0-10):',
  130. toggleActionMode: 'Chọn chế độ kích hoạt:\n1: Nhấn lâu\n2: Kéo thả\n0: Cả hai',
  131. setWindowSizeprompt: 'Nhập kích thước cửa sổ (pixel):',
  132. showCountdown: 'Hiển thị thanh tiến trình đếm ngược',
  133. saveWindowConfig: 'Ghi lại vị trí cửa sổ',
  134. showCountdowndrag: 'Hiển thị thanh tiến trình quá hạn khi kéo thả',
  135. dragTimeOut: 'Thời gian quá hạn khi kéo thả',
  136. }
  137. }
  138. // 返回翻译函数
  139. return (id, lang = '') => {
  140. const selectedLang = lang || userLang
  141. return (strings[selectedLang] || strings.en)[id] || strings.en[id]
  142. }
  143. }());
  144. (function () {
  145. 'use strict'
  146. const state = {
  147. isDragging: false,
  148. linkToPreload: null,
  149. popupWindow: null,
  150. acrylicOverlay: null,
  151. progressBar: null,
  152. dragprogressBar: null,
  153. dragintervalId: null,
  154. startTime: null,
  155. }
  156. const config = {
  157. windowWidth: GM_getValue('windowWidth', 870),
  158. windowHeight: GM_getValue('windowHeight', 530),
  159. screenLeft: (GM_getValue('screenLeft', 0) === 0)
  160. ? (window.screen.width - GM_getValue('windowWidth', 870)) / 2
  161. : GM_getValue('screenLeft'),
  162. screenTop: (GM_getValue('screenTop', 0) === 0)
  163. ? (window.screen.height - GM_getValue('windowHeight', 530)) / 3
  164. : GM_getValue('screenTop'),
  165. blurIntensity: GM_getValue('blurIntensity', 5),
  166. blurEnabled: GM_getValue('blurEnabled', true),
  167. closeOnMouseClick: GM_getValue('closeOnMouseClick', true),
  168. closeOnScroll: GM_getValue('closeOnScroll', true),
  169. longPressDuration: GM_getValue('longPressDuration', 500), // 长按持续时间(毫秒)
  170. dragTimeOut: GM_getValue('dragTimeOut', 2000), // 拖拽超时时间(毫秒)
  171. actionMode: GM_getValue('actionMode', 0), // 0: 两者都用, 1: 长按, 2: 拖拽
  172. showCountdown: GM_getValue('showCountdown', true), // 是否显示倒计时进度条
  173. showCountdowndrag: GM_getValue('showCountdowndrag', true), // 是否显示拖拽倒计时进度条
  174. saveWindowConfig: GM_getValue('saveWindowConfig', false)//记住窗口位置,没啥用
  175. }
  176. function delay(ms) {
  177. return new Promise(resolve => setTimeout(resolve, ms))
  178. }
  179. async function preloadLink(link, attributes = {}) {
  180. const preloadElement = document.createElement('link')
  181. preloadElement.rel = 'preload'
  182. preloadElement.href = link
  183. preloadElement.as = '*/*'
  184. Object.assign(preloadElement, attributes)
  185. document.head.appendChild(preloadElement)
  186. await delay(1)
  187. }
  188. function createAcrylicOverlay() {
  189. const acrylicOverlay = document.createElement('div')
  190. acrylicOverlay.style.position = 'fixed'
  191. acrylicOverlay.style.top = '0'
  192. acrylicOverlay.style.left = '0'
  193. acrylicOverlay.style.width = '100%'
  194. acrylicOverlay.style.height = '100%'
  195. acrylicOverlay.style.zIndex = '9999'
  196. acrylicOverlay.style.backdropFilter = config.blurEnabled ? `blur(${config.blurIntensity}px)` : 'none'
  197. if (config.closeOnMouseClick) {
  198. acrylicOverlay.addEventListener('click', handleAcrylicOverlayClick)
  199. }
  200. document.body.appendChild(acrylicOverlay)
  201. return acrylicOverlay
  202. }
  203. function handleAcrylicOverlayClick(event) {
  204. if (event.target === state.acrylicOverlay) {
  205. closePopupWindow()
  206. }
  207. }
  208. function removeAcrylicOverlay() {
  209. if (state.acrylicOverlay) {
  210. document.body.removeChild(state.acrylicOverlay)
  211. state.acrylicOverlay = null
  212. }
  213. }
  214. function openPopupWindow(link) {
  215. if (!state.popupWindow || state.popupWindow.closed) {
  216. state.acrylicOverlay = createAcrylicOverlay()
  217. state.popupWindow = window.open(link, '_blank', `width=${config.windowWidth},height=${config.windowHeight},left=${config.screenLeft},top=${config.screenTop}`)
  218. // console.log('Popup window:', state.popupWindow)
  219. state.popupWindowChecker = setInterval(() => {
  220. if (state.popupWindow) {//保证窗口存在时才检测,兼容下原来脚本点击原窗口焦点关闭覆盖层
  221. if (state.popupWindow.closed) {
  222. removeAcrylicOverlay()
  223. clearInterval(state.popupWindowChecker)
  224. } else {
  225. const width = state.popupWindow.innerWidth
  226. const height = state.popupWindow.innerHeight
  227. const left = state.popupWindow.screenX
  228. const top = state.popupWindow.screenY
  229. /* console.log(`Popup window size: width=${width}, height=${height}`)
  230. console.log(`Popup window position: left=${left}, top=${top}`) */
  231. if (config.saveWindowConfig) {
  232. saveWindowConfig(width, height, left, top)
  233. }
  234. }
  235. }
  236. }, 200)
  237. }
  238. }
  239. function closePopupWindow() {
  240. if (state.popupWindow && !state.popupWindow.closed) {
  241. state.popupWindow.close()
  242. state.popupWindow = null
  243. removeAcrylicOverlay()
  244. if (state.linkToPreload) {
  245. removePreloadedLink(state.linkToPreload)
  246. }
  247. window.removeEventListener('scroll', closePopupOnScroll)
  248. }
  249. }
  250. function removePreloadedLink(link) {
  251. const preloadElement = document.querySelector(`link[href="${link}"]`)
  252. if (preloadElement) {
  253. document.head.removeChild(preloadElement)
  254. }
  255. }
  256. function closePopupOnScroll() {
  257. if (state.popupWindow && !state.popupWindow.closed) {
  258. closePopupWindow()
  259. }
  260. }
  261. function toggleActionMode() {
  262. const mode = prompt(translate('toggleActionMode'), config.actionMode)
  263. if (mode !== null) {
  264. config.actionMode = parseInt(mode, 10)
  265. GM_setValue('actionMode', config.actionMode)
  266. setupEventListeners()
  267. updateMenuCommands()
  268. }
  269. }
  270. function setLongPressDuration() {
  271. const duration = prompt(translate('setLongPressDuration'), config.longPressDuration)
  272. if (duration !== null) {
  273. config.longPressDuration = duration
  274. GM_setValue('longPressDuration', duration)
  275. updateMenuCommands()
  276. }
  277. }
  278. function setdragTimeOut() {
  279. const duration = prompt(translate('dragTimeOut'), config.dragTimeOut)
  280. if (duration !== null) {
  281. config.dragTimeOut = duration
  282. GM_setValue('dragTimeOut', duration)
  283. updateMenuCommands()
  284. }
  285. }
  286. function toggleBlurEffect() {
  287. config.blurEnabled = !config.blurEnabled
  288. GM_setValue('blurEnabled', config.blurEnabled)
  289. updateMenuCommands()
  290. }
  291. function setBlurIntensity() {
  292. const intensity = prompt(translate('setBlurIntensityprompt'), config.blurIntensity)
  293. if (intensity !== null) {
  294. config.blurIntensity = parseInt(intensity, 10)
  295. GM_setValue('blurIntensity', config.blurIntensity)
  296. updateMenuCommands()
  297. }
  298. }
  299. function toggleCloseOnMouseClick() {
  300. config.closeOnMouseClick = !config.closeOnMouseClick
  301. GM_setValue('closeOnMouseClick', config.closeOnMouseClick)
  302. updateMenuCommands()
  303. }
  304. function toggleCloseOnScroll() {
  305. config.closeOnScroll = !config.closeOnScroll
  306. handleScrollCommand()
  307. GM_setValue('closeOnScroll', config.closeOnScroll)
  308. updateMenuCommands()
  309. }
  310. function handleScrollCommand() {
  311. if (config.closeOnScroll) {
  312. window.addEventListener('scroll', closePopupOnScroll, { once: true })
  313. } else {
  314. window.removeEventListener('scroll', closePopupOnScroll)
  315. }
  316. }
  317. function setWindowSize(dimension) {
  318. const size = prompt(`${translate('setWindowSizeprompt')} (${dimension})`, config[dimension === 'width' ? 'windowWidth' : 'windowHeight'])
  319. if (size !== null) {
  320. config[dimension === 'width' ? 'windowWidth' : 'windowHeight'] = parseInt(size, 10)
  321. GM_setValue(dimension === 'width' ? 'windowWidth' : 'windowHeight', config[dimension === 'width' ? 'windowWidth' : 'windowHeight'])
  322. updateMenuCommands()
  323. if (state.popupWindow && !state.popupWindow.closed) {
  324. state.popupWindow.resizeTo(config.windowWidth, config.windowHeight)
  325. }
  326. }
  327. }
  328. let registeredMenuCommands = {}
  329. function registerMenuCommand(label, action) {
  330. const menuCommandId = GM_registerMenuCommand(label, action)
  331. registeredMenuCommands[label] = menuCommandId
  332. return menuCommandId
  333. }
  334. function toggleshowCountdown() {
  335. config.showCountdown = !config.showCountdown
  336. GM_setValue('showCountdown', config.showCountdown)
  337. updateMenuCommands()
  338. }
  339. function saveWindowConfig(width, height, left, top) {
  340. config.windowHeight = height
  341. config.windowWidth = width
  342. config.screenLeft = left
  343. config.screenTop = top
  344. GM_setValue('windowWidth', width)
  345. GM_setValue('windowHeight', height)
  346. GM_setValue('screenLeft', left)
  347. GM_setValue('screenTop', top)
  348. updateMenuCommands()
  349. }
  350. function toggleSwitch(property) {
  351. if (property in config) {
  352. config[property] = !config[property]
  353. GM_setValue(property, config[property])
  354. updateMenuCommands()
  355. }
  356. }
  357. function updateMenuCommands() {
  358. const menuCommands = [
  359. { label: translate('actionMode') + ` (${config.actionMode === 1 ? translate('actionMode1') : config.actionMode === 2 ? translate('actionMode2') : translate('actionMode0')})`, action: toggleActionMode },
  360. { label: translate('longPressDuration') + ` (${config.longPressDuration}ms)`, action: setLongPressDuration },
  361. { label: translate('dragTimeOut') + ` (${config.dragTimeOut}ms)`, action: setdragTimeOut },
  362. { label: translate('blurEnabled') + ` (${config.blurEnabled ? '✅' : '❌'})`, action: toggleBlurEffect },
  363. { label: translate('blurIntensity') + ` (${config.blurIntensity})`, action: setBlurIntensity },
  364. { label: translate('closeOnMouseClick') + ` (${config.closeOnMouseClick ? '✅' : '❌'})`, action: toggleCloseOnMouseClick },
  365. { label: translate('closeOnScroll') + ` (${config.closeOnScroll ? '✅' : '❌'})`, action: toggleCloseOnScroll },
  366. { label: translate('windowWidth') + ` (${config.windowWidth})`, action: () => { setWindowSize('width') } },
  367. { label: translate('windowHeight') + ` (${config.windowHeight})`, action: () => { setWindowSize('height') } },
  368. { label: translate('showCountdown') + ` (${config.showCountdown ? '✅' : '❌'})`, action: () => { toggleSwitch('showCountdown') } },
  369. { label: translate('showCountdowndrag') + ` (${config.showCountdowndrag ? '✅' : '❌'})`, action: () => { toggleSwitch('showCountdowndrag') } },
  370. { label: translate('saveWindowConfig') + ` (${config.saveWindowConfig ? '✅' : '❌'})`, action: () => { toggleSwitch('saveWindowConfig') } },
  371. ]
  372. for (const label in registeredMenuCommands) {
  373. GM_unregisterMenuCommand(registeredMenuCommands[label])
  374. }
  375. registeredMenuCommands = {}
  376. menuCommands.forEach((command) => {
  377. registerMenuCommand(command.label, command.action)
  378. })
  379. }
  380. updateMenuCommands()
  381. function toTitleCase(str) {
  382. return str.replace(/\w\S*/g, (txt) => { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase() })
  383. }
  384. function setupEventListeners() {
  385. // 移除旧的事件监听器
  386. document.body.removeEventListener('dragstart', handleDragStart)
  387. document.body.removeEventListener('dragend', handleDragEnd)
  388. document.body.removeEventListener('mousedown', handleMouseDown)
  389. document.body.removeEventListener('mouseup', handleMouseUp)
  390. document.body.removeEventListener('mouseleave', handleMouseLeave)
  391. document.body.removeEventListener('wheel', handleWheel)
  392. document.body.removeEventListener('click', handleClick)
  393. // 根据 actionMode 配置添加事件监听器
  394. if (config.actionMode === 1 || config.actionMode === 0) {
  395. document.body.addEventListener('mousedown', handleMouseDown)
  396. document.body.addEventListener('mouseup', handleMouseUp)
  397. document.body.addEventListener('mouseleave', handleMouseLeave)
  398. }
  399. if (config.actionMode === 2 || config.actionMode === 0) {
  400. document.body.addEventListener('dragstart', handleDragStart)
  401. document.body.addEventListener('dragend', handleDragEnd)
  402. }
  403. document.body.addEventListener('wheel', handleWheel)
  404. document.body.addEventListener('click', handleClick)
  405. }
  406. // 事件处理函数
  407. function handleDragStart(event) {
  408. const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a')
  409. if (linkElement) {
  410. if (config.showCountdowndrag && config.dragTimeOut != 0) {//超时选项,只要
  411. state.dragprogressBar = createProgressBar('#ff9800', '#f44336')
  412. state.dragprogressBar.style.display = 'block'
  413. state.dragprogressBar.style.width = '5%'
  414. state.startTime = Date.now()
  415. clearInterval(state.dragintervalId)
  416. state.dragintervalId = setInterval(function () {
  417. const elapsed = Date.now() - state.startTime
  418. const progress = Math.max(5 - (elapsed / config.dragTimeOut) * 5, 0) // 减小你妈
  419. state.dragprogressBar.style.width = `${progress}%`
  420. if (progress <= 0) {// 超时结束
  421. state.isDragging = false
  422. clearInterval(state.dragintervalId)
  423. state.dragprogressBar.style.display = 'none'
  424. }
  425. }, 100) //
  426. window.addEventListener('drag', function (event) {
  427. // 保证进度条位置处于貂毛鼠标的下面
  428. const x = event.clientX
  429. const y = event.clientY + 30 // 偏移
  430. state.dragprogressBar.style.left = `${x}px`
  431. state.dragprogressBar.style.top = `${y}px`
  432. })
  433. }
  434. const link = linkElement.href
  435. state.isDragging = true
  436. state.linkToPreload = link
  437. preloadLink(state.linkToPreload, { importance: 'high' }).then(() => {
  438. if (config.closeOnScroll) {
  439. window.addEventListener('scroll', closePopupOnScroll, { once: true })
  440. }
  441. })
  442. }
  443. }
  444. function handleDragEnd() {
  445. if (state.dragprogressBar) {//显示超时进度条时
  446. clearInterval(state.dragintervalId)
  447. state.dragprogressBar.style.display = 'none'
  448. }
  449. if (state.isDragging && state.linkToPreload) {
  450. state.isDragging = false
  451. openPopupWindow(state.linkToPreload)
  452. state.linkToPreload = null
  453. }
  454. }
  455. function createProgressBar(colorStart = '#4caf50', colorEnd = '#81c784') {
  456. if (!config.showCountdown && !config.showCountdowndrag) return null
  457. const progressBar = document.createElement('div')
  458. Object.assign(progressBar.style, {
  459. position: 'fixed',
  460. height: '6px',
  461. width: '5%',
  462. background: `linear-gradient(to right, ${colorStart}, ${colorEnd})`,
  463. borderRadius: '3px',
  464. boxShadow: '0 2px 5px rgba(0, 0, 0, 0.3)',
  465. zIndex: '9999',
  466. })
  467. document.body.appendChild(progressBar)
  468. return progressBar
  469. }
  470. let mouseDownTime = 0
  471. function handleMouseDown(event) {
  472. const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a')
  473. if (linkElement) {
  474. let isDragging = false
  475. let isMouseDown = true
  476. const onMouseMove = () => {
  477. isDragging = true
  478. clearTimeout(state.pressTimer)
  479. progressBarremove()
  480. }
  481. const onMouseUp = () => {
  482. isMouseDown = false
  483. clearTimeout(state.pressTimer)
  484. progressBarremove()
  485. }
  486. document.addEventListener('dragstart', onMouseMove, { once: true })
  487. document.addEventListener('mouseup', onMouseUp, { once: true })
  488. setTimeout(() => { // 按下100ms后显示倒计时,避免点击就显示
  489. if (!isDragging && isMouseDown) { // 确保没有拖拽并且鼠标仍按下
  490. state.progressBar = createProgressBar()
  491. if (state.progressBar) {
  492. const transitionDuration = Math.max(config.longPressDuration - 100, 0) + 'ms'
  493. state.progressBar.style.left = `${event.clientX}px` // 设置进度条位置为鼠标下方
  494. state.progressBar.style.top = `${event.clientY + 20}px` // 偏移一点,避免挡住鼠标
  495. state.progressBar.style.transition = `width ${transitionDuration} linear`
  496. requestAnimationFrame(() => {
  497. state.progressBar.style.width = '0'
  498. })
  499. }
  500. }
  501. }, 100)
  502. state.pressTimer = setTimeout(() => {
  503. if (!isDragging && isMouseDown) { // 确保没有拖拽并且鼠标仍按下
  504. const link = linkElement.href
  505. state.linkToPreload = link
  506. preloadLink(state.linkToPreload, { importance: 'high' }).then(() => {
  507. openPopupWindow(state.linkToPreload)
  508. })
  509. }
  510. progressBarremove()
  511. }, config.longPressDuration)
  512. }
  513. }
  514. function handleMouseUp() {
  515. clearTimeout(state.pressTimer)
  516. state.pressTimer = null
  517. progressBarremove()
  518. }
  519. function progressBarremove() {
  520. if (state.progressBar) {
  521. state.progressBar.remove()
  522. }
  523. }
  524. function handleMouseLeave() {
  525. clearTimeout(state.pressTimer)
  526. state.pressTimer = null
  527. }
  528. function handleWheel() {
  529. if (config.closeOnScroll) {
  530. closePopupWindow()
  531. }
  532. }
  533. function handleClick(event) {
  534. if (event.target === state.acrylicOverlay) {
  535. removeAcrylicOverlay()
  536. }
  537. }
  538. setupEventListeners()
  539. })()

QingJ © 2025

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