YYPOST群发软件 发表于 2026-8-18 07:41:19

无障碍方式双指向中间滑动放大缩小完整示例

无障碍方式双指向中间滑动放大缩小完整示例


/**
* ============================================================
* AIWROK - 双指向中间滑动(捏合手势)完整示例【无碍模式】
* ============================================================
* 核心功能:
*   1. auto.dispatchGesture()—— 同时执行多条路径,实现多点触控
*   2. new path() + setDurTime() + addPoint() —— 构建单根手指的滑动轨迹
*
* 手势说明:
*   - 捏合(缩小):左指向右滑 + 右指向左滑,两指同时向中间靠拢
*   - 放大(张开):左指向左滑 + 右指向右滑,两指同时向外分开(附带示例)
*
* 无碍模式特性:
*   1. 环境自检:运行前自动检查 path / auto.dispatchGesture 是否可用
*   2. 安全封装:创建路径、执行手势全部走 try-catch,任何一步失败都不崩溃
*   3. 参数校验:百分比参数越界自动纠正到合法范围
*   4. 单个手势失败不影响后续手势继续执行
*   5. 失败降级:返回 false 并给出排查建议,方便上层逻辑做备选方案
*
* 【重要】本文件仅适配"无碍模式"(无障碍服务)运行:
*   - 手势一律使用 auto.dispatchGesture() + new path()
*   - 运行前请确认已开启 AIWROK 无障碍服务权限
* ============================================================
*/

// =====================================================
//0. 环境自检(脚本运行时会自动执行一次)
// =====================================================
var _GESTURE_OK = true;

(function _gestureSelfCheck() {
    var msg = "【自检】捏合手势运行环境:\n";
    try {
      if (typeof path === 'undefined') {
            msg += "× path 对象不可用(无法创建手势路径)\n";
            _GESTURE_OK = false;
      } else {
            msg += "✓ path 对象可用\n";
      }

      if (typeof auto === 'undefined' || typeof auto.dispatchGesture !== 'function') {
            msg += "× auto.dispatchGesture 不可用(无法执行手势)\n";
            _GESTURE_OK = false;
      } else {
            msg += "✓ auto.dispatchGesture 可用\n";
      }

      // 屏幕尺寸检测(可选,失败可用默认值)
      try {
            if (typeof screen !== 'undefined'
                && typeof screen.getScreenWidth === 'function'
                && typeof screen.getScreenHeight === 'function') {
                msg += "✓ screen 尺寸可用: " + screen.getScreenWidth() + " x " + screen.getScreenHeight() + "\n";
            } else if (typeof device !== 'undefined' && typeof device.width === 'number') {
                msg += "✓ device 尺寸可用: " + device.width + " x " + device.height + "\n";
            } else {
                msg += "⚠ 尺寸获取不可用,将回退 1080 x 1920\n";
            }
      } catch (e) {
            msg += "⚠ 尺寸读取异常: " + e + ",将回退 1080 x 1920\n";
      }

      if (_GESTURE_OK) {
            msg += "✓ 自检通过,可以正常执行捏合手势";
      } else {
            msg += "✗ 自检未通过!请检查是否已开启 AIWROK 无障碍服务权限";
      }
    } catch (e) {
      msg += "✗ 自检过程异常: " + e + "\n";
      _GESTURE_OK = false;
    }
    _log(msg);
})();

// =====================================================
//1. 安全工具函数
// =====================================================

/**
* 安全获取屏幕尺寸(任何异常都回退默认值)
*/
function _gestureGetScreen() {
    var w = 1080, h = 1920;
    try {
      if (typeof screen !== 'undefined') {
            if (typeof screen.getScreenWidth === 'function')w = screen.getScreenWidth();
            if (typeof screen.getScreenHeight === 'function') h = screen.getScreenHeight();
      } else if (typeof device !== 'undefined') {
            if (typeof device.width === 'number')w = device.width;
            if (typeof device.height === 'number') h = device.height;
      }
      // 防脏数据:尺寸为 0 或非数字时回退默认
      w = (typeof w === 'number' && w > 0) ? w : 1080;
      h = (typeof h === 'number' && h > 0) ? h : 1920;
    } catch (e) {
      _log("⚠ 获取屏幕尺寸异常,使用默认值: " + e);
    }
    return { w: w, h: h };
}

/**
* 参数校验:把百分比约束到 0~1 之间(防止越界导致手势报错)
*/
function _clampPercent(v, fallback) {
    try {
      v = parseFloat(v);
      if (isNaN(v)) return fallback;
      if (v < 0) return 0;
      if (v > 1) return 1;
      return v;
    } catch (e) {
      return fallback;
    }
}

/**
* 安全创建单指路径
* @param points 坐标点数组,如 [,]
* @param duration 时长(毫秒)
* @return path 对象;创建失败返回 null
*/
function _safeCreatePath(points, duration) {
    try {
      if (typeof path === 'undefined') {
            _log("✗ path 对象不可用,无法创建路径");
            return null;
      }
      if (!points || points.length < 2) {
            _log("✗ 路径点不足(至少需要 2 个点)");
            return null;
      }
      var p = new path();
      p.setDurTime(duration > 0 ? duration : 600);
      for (var i = 0; i < points.length; i++) {
            var pt = points;
            if (!pt || typeof pt !== 'number' || typeof pt !== 'number') {
                _log("✗ 路径点[" + i + "]非法,跳过该点: " + JSON.stringify(pt));
                continue;
            }
            p.addPoint(pt, pt);
      }
      return p;
    } catch (e) {
      _log("✗ 创建路径异常: " + e);
      return null;
    }
}

/**
* 安全执行手势(无碍核心:任何情况都不抛出异常)
* @param paths path 对象数组
* @return true/false
*/
function _safeDispatchGesture(paths) {
    try {
      if (!_GESTURE_OK) {
            _log("✗ 环境自检未通过(path/dispatchGesture 不可用),跳过手势");
            return false;
      }
      if (!paths || paths.length === 0) {
            _log("✗ 手势路径数组为空,不执行");
            return false;
      }
      if (typeof auto === 'undefined' || typeof auto.dispatchGesture !== 'function') {
            _log("✗ auto.dispatchGesture 不可用,无法执行手势");
            return false;
      }
      // 过滤掉创建失败(null)的路径,至少要有一条有效路径
      var valid = [];
      for (var i = 0; i < paths.length; i++) {
            if (paths) valid.push(paths);
      }
      if (valid.length === 0) {
            _log("✗ 没有有效的路径对象,不执行手势");
            return false;
      }
      var result = auto.dispatchGesture(valid);
      if (result === true || result === 1) {
            return true;
      }
      _log("✗ dispatchGesture 返回失败(可能是权限不足或当前界面不支持手势)");
      return false;
    } catch (e) {
      _log("✗ 执行手势异常: " + e);
      return false;
    }
}

/**
* 手势失败时的排查提示(无碍模式:失败不静默,给线索)
*/
function _gestureFailHint() {
    _log("└─ 排查建议:");
    _log("      1. 确认已开启 AIWROK 的【无障碍服务】权限");
    _log("      2. 确认当前处于支持双指捏合的界面(如看图/地图,桌面可能没反应)");
    _log("      3. 确认无障碍服务没有与其他应用冲突");
}

/**
* 日志工具:兼容 print.log / console.log
*/
function _log(msg) {
    try {
      if (typeof print !== 'undefined' && typeof print.log === 'function') {
            print.log(msg);
      } else if (typeof console !== 'undefined' && typeof console.log === 'function') {
            console.log(msg);
      }
    } catch (e) {
      // 日志失败直接忽略,不影响主体功能
    }
}

// =====================================================
//2. 无碍模式捏合工具函数(可直接复用)
// =====================================================

/**
* 水平捏合(左指向右 + 右指向左 → 向中间滑动)
* @param leftStartXPercent左指起点 X 百分比 0~1
* @param rightStartXPercent 右指起点 X 百分比 0~1
* @param yPercent         捏合中心线 Y 百分比 0~1
* @param convergePercent    向中间收敛位置(0.5 = 正中心)
* @param duration         时长(毫秒)
*/
function pinchHorizontal(leftStartXPercent, rightStartXPercent, yPercent, convergePercent, duration) {
    try {
      leftStartXPercent= _clampPercent(leftStartXPercent, 0.25);
      rightStartXPercent = _clampPercent(rightStartXPercent, 0.75);
      yPercent         = _clampPercent(yPercent, 0.5);
      convergePercent    = _clampPercent(convergePercent, 0.5);
      duration         = (typeof duration === 'number' && duration > 0) ? duration : 600;

      var s = _gestureGetScreen();
      var y= s.h * yPercent;
      var cx = s.w * convergePercent;
      var gap = s.w * 0.05;

      // 左指:起点 → 中心左侧(留间距不撞车)
      var pL = _safeCreatePath([
            ,
            
      ], duration);
      // 右指:起点 → 中心右侧
      var pR = _safeCreatePath([
            ,
            
      ], duration);

      _log("水平捏合: 左 " + (leftStartXPercent * 100).toFixed(0) + "% → 中心左"
             + ",右 " + (rightStartXPercent * 100).toFixed(0) + "% → 中心右"
             + ",Y=" + (yPercent * 100).toFixed(0) + "%,时长 " + duration + "ms");

      var ok = _safeDispatchGesture();
      _log(ok ? "✓ 水平捏合执行成功" : "✗ 水平捏合执行失败");
      if (!ok) _gestureFailHint();
      return ok;
    } catch (e) {
      _log("✗ 水平捏合函数异常: " + e);
      return false;
    }
}

/**
* 垂直捏合(上指下滑 + 下指上滑 → 向中间)
* @param topStartYPercent    上指起点 Y 百分比 0~1
* @param bottomStartYPercent 下指起点 Y 百分比 0~1
* @param xPercent            捏合中心线 X 百分比 0~1
* @param convergePercent   向中间收敛位置(0.5 = 正中心)
* @param duration            时长(毫秒)
*/
function pinchVertical(topStartYPercent, bottomStartYPercent, xPercent, convergePercent, duration) {
    try {
      topStartYPercent    = _clampPercent(topStartYPercent, 0.25);
      bottomStartYPercent = _clampPercent(bottomStartYPercent, 0.75);
      xPercent            = _clampPercent(xPercent, 0.5);
      convergePercent   = _clampPercent(convergePercent, 0.5);
      duration            = (typeof duration === 'number' && duration > 0) ? duration : 600;

      var s = _gestureGetScreen();
      var x= s.w * xPercent;
      var cy = s.h * convergePercent;
      var gap = s.h * 0.05;

      // 上指:顶部 → 中心上方
      var pT = _safeCreatePath([
            ,
            
      ], duration);
      // 下指:底部 → 中心下方
      var pB = _safeCreatePath([
            ,
            
      ], duration);

      _log("垂直捏合: 上 " + (topStartYPercent * 100).toFixed(0) + "% + 下 "
             + (bottomStartYPercent * 100).toFixed(0) + "% → 中心,X="
             + (xPercent * 100).toFixed(0) + "%,时长 " + duration + "ms");

      var ok = _safeDispatchGesture();
      _log(ok ? "✓ 垂直捏合执行成功" : "✗ 垂直捏合执行失败");
      if (!ok) _gestureFailHint();
      return ok;
    } catch (e) {
      _log("✗ 垂直捏合函数异常: " + e);
      return false;
    }
}

/**
* 对角线捏合(左上 + 右下 → 中心,看图缩小最常用)
* @param duration 时长(毫秒)
*/
function pinchDiagonal(duration) {
    try {
      duration = (typeof duration === 'number' && duration > 0) ? duration : 700;
      var s = _gestureGetScreen();
      var cx = s.w * 0.5, cy = s.h * 0.5;
      var gx = s.w * 0.05, gy = s.h * 0.05;

      var p1 = _safeCreatePath([
            ,
            
      ], duration);
      var p2 = _safeCreatePath([
            ,
            
      ], duration);

      _log("对角线捏合: 左上(15%,20%) + 右下(85%,80%) → 中心,时长 " + duration + "ms");

      var ok = _safeDispatchGesture();
      _log(ok ? "✓ 对角线捏合执行成功" : "✗ 对角线捏合执行失败");
      if (!ok) _gestureFailHint();
      return ok;
    } catch (e) {
      _log("✗ 对角线捏合函数异常: " + e);
      return false;
    }
}

/**
* 通用双指捏合(任意起点 + 指定收敛中心)
* @param start1   起点1 {x:0~1, y:0~1}
* @param start2   起点2 {x:0~1, y:0~1}
* @param center   收敛中心 {x:0~1, y:0~1}
* @param duration 时长(毫秒)
*/
function pinchGeneric(start1, start2, center, duration) {
    try {
      if (!start1 || !start2 || typeof start1.x !== 'number' || typeof start2.x !== 'number') {
            _log("✗ pinchGeneric 参数错误:start1/start2 必填且必须含 x/y 百分比");
            return false;
      }
      var s = _gestureGetScreen();
      center   = center   || { x: 0.5, y: 0.5 };
      duration = (typeof duration === 'number' && duration > 0) ? duration : 700;
      var ratio = 0.85; // 向中心靠近比例(留间距避免两指重合被识别成单指)

      var cx = s.w * _clampPercent(center.x, 0.5);
      var cy = s.h * _clampPercent(center.y, 0.5);

      var p1 = _safeCreatePath([
            ,
            [s.w * _clampPercent(start1.x, 0.25) + (cx - s.w * _clampPercent(start1.x, 0.25)) * ratio,
             s.h * _clampPercent(start1.y, 0.25) + (cy - s.h * _clampPercent(start1.y, 0.25)) * ratio]
      ], duration);

      var p2 = _safeCreatePath([
            ,
            [s.w * _clampPercent(start2.x, 0.75) + (cx - s.w * _clampPercent(start2.x, 0.75)) * ratio,
             s.h * _clampPercent(start2.y, 0.75) + (cy - s.h * _clampPercent(start2.y, 0.75)) * ratio]
      ], duration);

      _log("通用捏合: (" + start1.x + "," + start1.y + ") + (" + start2.x + "," + start2.y
             + ") → 中心(" + center.x + "," + center.y + "),时长 " + duration + "ms");

      var ok = _safeDispatchGesture();
      _log(ok ? "✓ 通用捏合执行成功" : "✗ 通用捏合执行失败");
      if (!ok) _gestureFailHint();
      return ok;
    } catch (e) {
      _log("✗ 通用捏合函数异常: " + e);
      return false;
    }
}

/**
* 水平放大(与捏合相反,双指向外分开)
* @param duration 时长(毫秒)
*/
function zoomHorizontal(duration) {
    try {
      duration = (typeof duration === 'number' && duration > 0) ? duration : 600;
      var s = _gestureGetScreen();
      var y   = s.h * 0.5;
      var gap = s.w * 0.05;

      var pL = _safeCreatePath([
            ,
            
      ], duration);
      var pR = _safeCreatePath([
            ,
            
      ], duration);

      _log("水平放大: 中间 → 左右张开,时长 " + duration + "ms");

      var ok = _safeDispatchGesture();
      _log(ok ? "✓ 水平放大执行成功" : "✗ 水平放大执行失败");
      if (!ok) _gestureFailHint();
      return ok;
    } catch (e) {
      _log("✗ 水平放大函数异常: " + e);
      return false;
    }
}

// =====================================================
//3. 主流程(无碍模式:每个示例独立 try-catch,互不影响)
// =====================================================
function main() {
    _log("========== 双指向中间滑动(捏合手势)示例【无碍模式】 ==========");

    if (!_GESTURE_OK) {
      _log("✗ 环境自检未通过,跳过所有手势执行(请先开启无障碍服务权限)");
      return;
    }

    var s = _gestureGetScreen();
    var screenW = s.w, screenH = s.h;
    _log("屏幕尺寸: " + screenW + " x " + screenH);

    // ---------- 示例 1:水平方向双指捏合(向中间滑动,缩小图片用) ----------
    _log("\n--- 示例1: 水平捏合(向中间滑动,缩小)---");
    try {
      var pathLeft1 = new path();
      pathLeft1.setDurTime(600);
      pathLeft1.addPoint(screenW * 0.25, screenH * 0.5);
      pathLeft1.addPoint(screenW * 0.45, screenH * 0.5);

      var pathRight1 = new path();
      pathRight1.setDurTime(600);
      pathRight1.addPoint(screenW * 0.75, screenH * 0.5);
      pathRight1.addPoint(screenW * 0.55, screenH * 0.5);

      var ok1 = _safeDispatchGesture();
      _log(ok1 ? "✓ 水平捏合执行成功" : "✗ 水平捏合执行失败");
      if (!ok1) _gestureFailHint();
    } catch (e) {
      _log("✗ 示例1 异常: " + e);
    }
    try { sleep.second(1); } catch (e) {}

    // ---------- 示例 2:垂直方向双指捏合(上指下滑 + 下指上滑 → 向中间) ----------
    _log("\n--- 示例2: 垂直捏合(向中间滑动)---");
    try {
      var pathTop = new path();
      pathTop.setDurTime(600);
      pathTop.addPoint(screenW * 0.5, screenH * 0.25);
      pathTop.addPoint(screenW * 0.5, screenH * 0.45);

      var pathBottom = new path();
      pathBottom.setDurTime(600);
      pathBottom.addPoint(screenW * 0.5, screenH * 0.75);
      pathBottom.addPoint(screenW * 0.5, screenH * 0.55);

      var ok2 = _safeDispatchGesture();
      _log(ok2 ? "✓ 垂直捏合执行成功" : "✗ 垂直捏合执行失败");
      if (!ok2) _gestureFailHint();
    } catch (e) {
      _log("✗ 示例2 异常: " + e);
    }
    try { sleep.second(1); } catch (e) {}

    // ---------- 示例 3:对角线捏合(左上+右下同时向中心靠拢) ----------
    _log("\n--- 示例3: 对角线捏合(左上+右下 → 中心)---");
    try {
      var pathLT = new path();
      pathLT.setDurTime(700);
      pathLT.addPoint(screenW * 0.15, screenH * 0.2);
      pathLT.addPoint(screenW * 0.42, screenH * 0.46);

      var pathRB = new path();
      pathRB.setDurTime(700);
      pathRB.addPoint(screenW * 0.85, screenH * 0.8);
      pathRB.addPoint(screenW * 0.58, screenH * 0.54);

      var ok3 = _safeDispatchGesture();
      _log(ok3 ? "✓ 对角线捏合执行成功" : "✗ 对角线捏合执行失败");
      if (!ok3) _gestureFailHint();
    } catch (e) {
      _log("✗ 示例3 异常: " + e);
    }
    try { sleep.second(1); } catch (e) {}

    // ---------- 示例 4:放大手势(与捏合相反,双指向外分开) ----------
    _log("\n--- 示例4: 水平放大(双指向外分开)---");
    try {
      var pathLeft4 = new path();
      pathLeft4.setDurTime(600);
      pathLeft4.addPoint(screenW * 0.45, screenH * 0.5);
      pathLeft4.addPoint(screenW * 0.20, screenH * 0.5);

      var pathRight4 = new path();
      pathRight4.setDurTime(600);
      pathRight4.addPoint(screenW * 0.55, screenH * 0.5);
      pathRight4.addPoint(screenW * 0.80, screenH * 0.5);

      var ok4 = _safeDispatchGesture();
      _log(ok4 ? "✓ 水平放大执行成功" : "✗ 水平放大执行失败");
      if (!ok4) _gestureFailHint();
    } catch (e) {
      _log("✗ 示例4 异常: " + e);
    }
    try { sleep.second(1); } catch (e) {}

    // ---------- 示例 5:调用封装函数 ----------
    _log("\n--- 示例5: 调用封装函数 pinchHorizontal() ---");
    try {
      var ok5 = pinchHorizontal(0.2, 0.8, 0.5, 0.5, 800);
      _log(ok5 ? "✓ 封装函数调用成功" : "✗ 封装函数调用失败(不影响脚本继续)");
    } catch (e) {
      _log("✗ 示例5 异常: " + e);
    }
    try { sleep.second(1); } catch (e) {}

    // ---------- 示例 6:封装函数集合演示(垂直/对角线/放大/通用) ----------
    _log("\n--- 示例6: 封装函数集合演示 ---");
    try {
      pinchVertical(0.2, 0.8, 0.5, 0.5, 700);
      try { sleep.second(1); } catch (e) {}
      pinchDiagonal(800);
      try { sleep.second(1); } catch (e) {}
      zoomHorizontal(700);
      try { sleep.second(1); } catch (e) {}
      pinchGeneric({x: 0.1, y: 0.1}, {x: 0.9, y: 0.9}, {x: 0.5, y: 0.5}, 800);
    } catch (e) {
      _log("✗ 示例6 异常: " + e);
    }

    _log("\n========== 示例全部执行完成(无碍模式) ==========");
}

// 执行主函数
main();


页: [1]
查看完整版本: 无障碍方式双指向中间滑动放大缩小完整示例