fix: 大屏触控优化——①点击错位:ctrl-img object-fit:contain 时图片绘制区小于盒子(居中黑边),坐标换算改为按实际绘制区域,黑边点击忽略;②滑动卡顿:tap/swipe/key/text 每次 u2.connect 重建连接(1-3s 握手),加 60s 连接缓存,失败自动清缓存重建(实测点击 2.97s→1.13s)

This commit is contained in:
2026-08-20 13:22:21 +08:00
parent 31a2ae911e
commit 1e39f5490a
2 changed files with 64 additions and 9 deletions
+9 -5
View File
@@ -391,14 +391,18 @@ function updateCtrlHead(){
document.getElementById('ctrl-sub').textContent=_ctrlSerial+(d.present?'':'(离线)');
}
// 坐标映射:显示尺寸 -> 设备原生分辨率
// 图片 object-fit:contain 时实际绘制区域小于盒子(居中留黑边),
// 必须按绘制区域换算,否则黑边/偏移导致点击错位
function ctrlCoords(ev){
const img=document.getElementById('ctrl-img');
if(!img.naturalWidth)return[null,null];
if(!img.naturalWidth||!img.naturalHeight)return[null,null];
const r=img.getBoundingClientRect();
return[
Math.round((ev.clientX-r.left)/r.width*img.naturalWidth),
Math.round((ev.clientY-r.top)/r.height*img.naturalHeight),
];
const scale=Math.min(r.width/img.naturalWidth,r.height/img.naturalHeight);
const drawW=img.naturalWidth*scale, drawH=img.naturalHeight*scale;
const ox=(r.width-drawW)/2, oy=(r.height-drawH)/2; // contain 偏移
const px=ev.clientX-r.left, py=ev.clientY-r.top;
if(px<ox||px>ox+drawW||py<oy||py>oy+drawH)return[null,null]; // 黑边点击忽略
return[Math.round((px-ox)/scale),Math.round((py-oy)/scale)];
}
function ctrlTap(x,y){
if(x==null||!x)return;
+55 -4
View File
@@ -416,10 +416,34 @@ def api_screen_stream():
mimetype="multipart/x-mixed-replace; boundary=frame")
_screen_u2_cache = {} # serial -> (ts, device):大屏高频触控复用连接(60s TTL)
def _screen_get_device(serial):
"""远程看屏用的 u2 连接(连接失败抛异常由调用方转 503)。"""
"""远程看屏用的 u2 连接(60s 缓存复用,避免每次 tap/swipe 都重建连接)。
大屏高频操作(点击/滑动/按键)每次 u2.connect 有 1-3s 握手开销(atx-agent
探测),体验卡顿。缓存 60s 内复用;连接失败(设备离线/重启)时清缓存重建,
由调用方把异常转 503。
"""
import time
import uiautomator2 as u2
return u2.connect(serial)
now = time.time()
cached = _screen_u2_cache.get(serial)
if cached and now - cached[0] < 60:
return cached[1]
d = u2.connect(serial)
_screen_u2_cache[serial] = (now, d)
if len(_screen_u2_cache) > 30: # 惰性清理过期项
for k in list(_screen_u2_cache):
if now - _screen_u2_cache[k][0] >= 60:
del _screen_u2_cache[k]
return d
def _screen_invalidate(serial):
"""u2 调用失败时清掉缓存(设备离线/atx-agent 重启),下次请求重新连接。"""
_screen_u2_cache.pop(serial, None)
@bp.route("/api/screen/thumb")
@perm_required(PERM_DEVICES)
@@ -464,6 +488,7 @@ def api_screen_tap():
_screen_get_device(serial).click(int(x), int(y))
return jsonify({"ok": True})
except Exception as e:
_screen_invalidate(serial)
return jsonify({"ok": False, "error": f"点击失败: {e}"}), 503
@bp.route("/api/screen/swipe", methods=["POST"])
@@ -480,6 +505,7 @@ def api_screen_swipe():
duration=float(data.get("duration", 0.2)))
return jsonify({"ok": True})
except Exception as e:
_screen_invalidate(serial)
return jsonify({"ok": False, "error": f"滑动失败: {e}"}), 503
@@ -498,6 +524,7 @@ def api_screen_key():
_screen_get_device(serial).press(key)
return jsonify({"ok": True})
except Exception as e:
_screen_invalidate(serial)
return jsonify({"ok": False, "error": f"按键失败: {e}"}), 503
@bp.route("/api/screen/text", methods=["POST"])
@@ -528,8 +555,32 @@ def _current_focus_pkg(serial):
except Exception:
return ""
_screen_u2_cache = {} # serial -> (ts, device):大屏高频触控复用连接(60s TTL)
def _screen_get_device(serial):
"""远程看屏用的 u2 连接(连接失败抛异常由调用方转 503)。"""
"""远程看屏用的 u2 连接(60s 缓存复用,避免每次 tap/swipe 都重建连接)。
大屏高频操作(点击/滑动/按键)每次 u2.connect 有 1-3s 握手开销(atx-agent
探测),体验卡顿。缓存 60s 内复用;连接失败(设备离线/重启)时清缓存重建,
由调用方把异常转 503。
"""
import time
import uiautomator2 as u2
return u2.connect(serial)
now = time.time()
cached = _screen_u2_cache.get(serial)
if cached and now - cached[0] < 60:
return cached[1]
d = u2.connect(serial)
_screen_u2_cache[serial] = (now, d)
if len(_screen_u2_cache) > 30: # 惰性清理过期项
for k in list(_screen_u2_cache):
if now - _screen_u2_cache[k][0] >= 60:
del _screen_u2_cache[k]
return d
def _screen_invalidate(serial):
"""u2 调用失败时清掉缓存(设备离线/atx-agent 重启),下次请求重新连接。"""
_screen_u2_cache.pop(serial, None)