scroll.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { ipcMain } from 'electron';
  2. import { exec } from 'child_process';
  3. import { promisify } from 'util';
  4. import { getCachedAdbPath } from '../config.js';
  5. const execAsync = promisify(exec);
  6. // 默认每次滚动距离(像素)
  7. const DEFAULT_SCROLL_DISTANCE = 100;
  8. // 默认滚动持续时间(毫秒),使用较长的时间模拟真实手指滑动
  9. const DEFAULT_SCROLL_DURATION = 500;
  10. /**
  11. * 计算滚动操作的坐标(小幅度滚动)
  12. * @param {string} direction - 滚动方向: up-down, down-up, left-right, right-left
  13. * @param {number} width - 设备宽度
  14. * @param {number} height - 设备高度
  15. * @param {number} scrollDistance - 每次滚动距离(像素),默认10px
  16. * @returns {Object} 包含起始和结束坐标的对象 {x1, y1, x2, y2}
  17. */
  18. function calculateScrollCoordinates(direction, width, height, scrollDistance = DEFAULT_SCROLL_DISTANCE) {
  19. // 滚动从屏幕中心开始
  20. const centerX = Math.round(width / 2);
  21. const centerY = Math.round(height / 2);
  22. let x1, y1, x2, y2;
  23. switch (direction) {
  24. case 'up-down':
  25. // 从上往下滚动(向下滚动)
  26. x1 = x2 = centerX;
  27. y1 = centerY;
  28. y2 = centerY + scrollDistance;
  29. break;
  30. case 'down-up':
  31. // 从下往上滚动(向上滚动)
  32. x1 = x2 = centerX;
  33. y1 = centerY;
  34. y2 = centerY - scrollDistance;
  35. break;
  36. case 'left-right':
  37. // 从左往右滚动(向右滚动)
  38. y1 = y2 = centerY;
  39. x1 = centerX;
  40. x2 = centerX + scrollDistance;
  41. break;
  42. case 'right-left':
  43. // 从右往左滚动(向左滚动)
  44. y1 = y2 = centerY;
  45. x1 = centerX;
  46. x2 = centerX - scrollDistance;
  47. break;
  48. default:
  49. throw new Error(`未知的滚动方向: ${direction}`);
  50. }
  51. return { x1, y1, x2, y2 };
  52. }
  53. /**
  54. * 发送滚动操作到设备
  55. * 使用触摸屏事件模拟真实的手指滑动,避免触发 tap 事件
  56. * @param {string} ipPort - 设备 ID
  57. * @param {string} direction - 滚动方向: up-down, down-up, left-right, right-left
  58. * @param {number} width - 设备宽度
  59. * @param {number} height - 设备高度
  60. * @param {number} scrollDistance - 每次滚动距离(像素),默认10px
  61. * @param {number} duration - 滚动持续时间(毫秒),默认500ms
  62. * @returns {Promise<Object>} 执行结果 {success, error?}
  63. */
  64. export async function sendScroll(ipPort, direction, width, height, scrollDistance = DEFAULT_SCROLL_DISTANCE, duration = DEFAULT_SCROLL_DURATION) {
  65. if (!ipPort) {
  66. return { success: false, error: '缺少设备 ID' };
  67. }
  68. if (!direction) {
  69. return { success: false, error: '缺少滚动方向' };
  70. }
  71. const validDirections = ['up-down', 'down-up', 'left-right', 'right-left'];
  72. if (!validDirections.includes(direction)) {
  73. return { success: false, error: `无效的滚动方向: ${direction},应为: ${validDirections.join(', ')}` };
  74. }
  75. if (typeof width !== 'number' || typeof height !== 'number') {
  76. return { success: false, error: '设备宽度和高度必须是数字' };
  77. }
  78. if (width <= 0 || height <= 0) {
  79. return { success: false, error: '设备宽度和高度必须大于0' };
  80. }
  81. try {
  82. const adbPath = getCachedAdbPath();
  83. // 计算滚动坐标
  84. const { x1, y1, x2, y2 } = calculateScrollCoordinates(direction, width, height, scrollDistance);
  85. // 使用 input touchscreen swipe 命令来模拟真实的触摸滑动
  86. // 这个命令会模拟手指在屏幕上慢慢滑动,不会触发 tap 事件
  87. // 使用较长的持续时间(至少 300ms)确保被识别为滑动而不是点击
  88. const actualDuration = Math.max(duration, 300); // 确保至少 300ms
  89. const command = `${adbPath} -s ${ipPort} shell input touchscreen swipe ${x1} ${y1} ${x2} ${y2} ${actualDuration}`;
  90. await execAsync(command, {
  91. timeout: 10000,
  92. maxBuffer: 1024 * 1024
  93. });
  94. console.log(`成功滚动: ${direction} (${x1},${y1}) -> (${x2},${y2}), 持续时间: ${actualDuration}ms`);
  95. return { success: true };
  96. } catch (error) {
  97. console.error('滚动失败:', error.message);
  98. return { success: false, error: error.message };
  99. }
  100. }
  101. // 注册 IPC 处理器
  102. export function registerIpcHandlers() {
  103. // IPC 处理程序:发送滚动操作到设备
  104. ipcMain.handle('send-scroll', async (event, ipPort, direction, width, height, scrollDistance, duration) => {
  105. return await sendScroll(ipPort, direction, width, height, scrollDistance, duration);
  106. });
  107. }