touch-event.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { useRef, useCallback, useState, useEffect } from 'react';
  2. // 触摸事件处理逻辑:坐标转换和手势模拟
  3. export function useTouchEvents(currentDevice, imageRef) {
  4. const isDragging = useRef(false);
  5. const startPos = useRef({ x: 0, y: 0 });
  6. const lastPos = useRef({ x: 0, y: 0 });
  7. const touchStartTime = useRef(0);
  8. const [deviceResolution, setDeviceResolution] = useState({ width: 1280, height: 2400 });
  9. // 获取设备实际分辨率
  10. useEffect(() => {
  11. const fetchDeviceResolution = async () => {
  12. if (!currentDevice || !window.electronAPI || !window.electronAPI.getDeviceResolution) {
  13. // 如果 API 不可用,使用默认值
  14. return;
  15. }
  16. try {
  17. const result = await window.electronAPI.getDeviceResolution(currentDevice);
  18. if (result?.success && result.width && result.height) {
  19. setDeviceResolution({ width: result.width, height: result.height });
  20. }
  21. } catch (err) {
  22. console.warn('获取设备分辨率失败,使用默认值:', err);
  23. }
  24. };
  25. if (currentDevice) {
  26. fetchDeviceResolution();
  27. }
  28. }, [currentDevice]);
  29. // 将鼠标坐标转换为设备坐标
  30. const convertToDeviceCoords = useCallback((clientX, clientY) => {
  31. if (!imageRef.current) {
  32. return null;
  33. }
  34. const img = imageRef.current;
  35. const rect = img.getBoundingClientRect();
  36. // 获取图片的实际显示尺寸(考虑 object-fit: contain)
  37. // 图片可能不会完全填满容器,需要计算实际显示区域
  38. let displayWidth, displayHeight, offsetX, offsetY;
  39. if (img.naturalWidth && img.naturalHeight) {
  40. // 获取图片的原始尺寸
  41. const imgAspect = img.naturalWidth / img.naturalHeight;
  42. const containerAspect = rect.width / rect.height;
  43. if (imgAspect > containerAspect) {
  44. // 图片更宽,以宽度为准(左右可能有黑边)
  45. displayWidth = rect.width;
  46. displayHeight = rect.width / imgAspect;
  47. offsetX = 0;
  48. offsetY = (rect.height - displayHeight) / 2;
  49. } else {
  50. // 图片更高,以高度为准(上下可能有黑边)
  51. displayHeight = rect.height;
  52. displayWidth = rect.height * imgAspect;
  53. offsetX = (rect.width - displayWidth) / 2;
  54. offsetY = 0;
  55. }
  56. } else {
  57. // 如果图片尺寸未知,使用容器尺寸
  58. displayWidth = rect.width;
  59. displayHeight = rect.height;
  60. offsetX = 0;
  61. offsetY = 0;
  62. }
  63. // 计算鼠标相对于实际显示区域的位置
  64. const relativeX = (clientX - rect.left - offsetX) / displayWidth;
  65. const relativeY = (clientY - rect.top - offsetY) / displayHeight;
  66. // 检查是否在有效显示区域内(0-1之间)
  67. if (relativeX < 0 || relativeX > 1 || relativeY < 0 || relativeY > 1) {
  68. // 点击在显示区域外(黑边区域),返回 null
  69. return null;
  70. }
  71. // 转换为设备坐标
  72. const deviceX = Math.round(relativeX * deviceResolution.width);
  73. const deviceY = Math.round(relativeY * deviceResolution.height);
  74. // 确保坐标在设备范围内
  75. const clampedX = Math.max(0, Math.min(deviceResolution.width - 1, deviceX));
  76. const clampedY = Math.max(0, Math.min(deviceResolution.height - 1, deviceY));
  77. return { x: clampedX, y: clampedY };
  78. }, [imageRef, deviceResolution]);
  79. // 发送 tap 事件到设备
  80. const sendTap = useCallback(async (x, y) => {
  81. if (!currentDevice || !window.electronAPI || !window.electronAPI.sendTap) {
  82. console.warn('Tap API 不可用或设备未连接');
  83. return;
  84. }
  85. try {
  86. const result = await window.electronAPI.sendTap(currentDevice, x, y);
  87. if (!result?.success) {
  88. console.error('Tap 失败:', result?.error);
  89. }
  90. } catch (err) {
  91. console.error('Tap 异常:', err);
  92. }
  93. }, [currentDevice]);
  94. // 发送 swipe 事件到设备
  95. const sendSwipe = useCallback(async (x1, y1, x2, y2, duration = 300) => {
  96. if (!currentDevice || !window.electronAPI || !window.electronAPI.sendSwipe) {
  97. console.warn('Swipe API 不可用或设备未连接');
  98. return;
  99. }
  100. try {
  101. const result = await window.electronAPI.sendSwipe(currentDevice, x1, y1, x2, y2, duration);
  102. if (!result?.success) {
  103. console.error('Swipe 失败:', result?.error);
  104. }
  105. } catch (err) {
  106. console.error('Swipe 异常:', err);
  107. }
  108. }, [currentDevice]);
  109. // 处理鼠标按下
  110. const handleMouseDown = useCallback((e) => {
  111. if (!currentDevice || !imageRef.current) return;
  112. e.preventDefault();
  113. isDragging.current = true;
  114. touchStartTime.current = Date.now();
  115. const deviceCoords = convertToDeviceCoords(e.clientX, e.clientY);
  116. if (deviceCoords) {
  117. startPos.current = deviceCoords;
  118. lastPos.current = deviceCoords;
  119. }
  120. }, [currentDevice, imageRef, convertToDeviceCoords]);
  121. // 处理鼠标移动
  122. const handleMouseMove = useCallback((e) => {
  123. if (!isDragging.current || !currentDevice || !imageRef.current) return;
  124. e.preventDefault();
  125. const deviceCoords = convertToDeviceCoords(e.clientX, e.clientY);
  126. if (deviceCoords) {
  127. lastPos.current = deviceCoords;
  128. }
  129. }, [currentDevice, imageRef, convertToDeviceCoords]);
  130. // 处理鼠标释放
  131. const handleMouseUp = useCallback((e) => {
  132. if (!isDragging.current || !currentDevice) return;
  133. e.preventDefault();
  134. isDragging.current = false;
  135. const touchDuration = Date.now() - touchStartTime.current;
  136. const distance = Math.sqrt(
  137. Math.pow(lastPos.current.x - startPos.current.x, 2) +
  138. Math.pow(lastPos.current.y - startPos.current.y, 2)
  139. );
  140. // 判断是 tap 还是 swipe
  141. // 如果移动距离小于 10 像素且时间小于 300ms,认为是 tap
  142. if (distance < 10 && touchDuration < 300) {
  143. // Tap 操作
  144. sendTap(startPos.current.x, startPos.current.y);
  145. } else if (distance >= 10) {
  146. // Swipe 操作
  147. const swipeDuration = Math.max(100, Math.min(1000, touchDuration));
  148. sendSwipe(
  149. startPos.current.x,
  150. startPos.current.y,
  151. lastPos.current.x,
  152. lastPos.current.y,
  153. swipeDuration
  154. );
  155. }
  156. }, [currentDevice, sendTap, sendSwipe]);
  157. // 处理鼠标离开(防止拖拽到外部时无法触发 mouseup)
  158. const handleMouseLeave = useCallback((e) => {
  159. if (isDragging.current) {
  160. handleMouseUp(e);
  161. }
  162. }, [handleMouseUp]);
  163. return {
  164. handleMouseDown,
  165. handleMouseMove,
  166. handleMouseUp,
  167. handleMouseLeave,
  168. };
  169. }