在鸿蒙(HarmonyOS)生态中,实现“视线追踪控制 UI 焦点”是一项涉及多模态感知与底层 UI 框架联动的复杂交互技术。其核心逻辑在于将硬件采集的眼球运动数据,精准映射为系统级的焦点控制指令。

以下是实现该功能的核心架构与高阶实战方案:

一、 核心架构:从“视线坐标”到“UI 焦点”的闭环

视线追踪交互依赖于“硬件感知 → 算法处理 → 焦点映射”的全链路协同:

  1. 高精度硬件采集:通过设备内置的红外摄像头或眼动传感器(如 120Hz 采样率),实时捕捉用户的瞳孔位置与视线方向。
  2. 坐标转换与映射:将获取到的归一化视线坐标(如 [-1, 1] 范围)转换为屏幕像素坐标,进而计算视线在屏幕上的落点。
  3. UI 焦点接管:结合鸿蒙的焦点控制机制,当视线落点命中某个可获焦组件时,主动触发焦点转移,实现“眼神即光标”。

二、 高阶实战:视线追踪与 UI 焦点联动

场景:在沉浸式 IDE 或无障碍应用中,用户仅凭眼球转动即可定位代码行、选中按钮或展开文档,实现无接触式代码导航。

import { eyeTracker } from '@ohos.eyeTracker';
import { FocusController } from '@kit.ArkUI';

export class GazeFocusManager {
    private tracker: eyeTracker.EyeTracker | null = null;
    private focusController: FocusController;

    constructor(focusController: FocusController) {
        this.focusController = focusController;
    }

    // 1. 启动眼动追踪服务
    startTracking() {
        this.tracker = eyeTracker.createEyeTracker();
        this.tracker.on('gaze', (gaze: eyeTracker.GazeData) => {
            // 将归一化坐标转换为屏幕像素坐标
            const screenX = gaze.x * Screen.width;
            const screenY = gaze.y * Screen.height;
            
            // 2. 命中测试:判断视线落点是否包含可获焦组件
            const targetId = this.hitTest(screenX, screenY);
            if (targetId) {
                // 3. 触发 UI 焦点转移
                this.focusController.requestFocus(targetId);
            }
        });
    }

    // 命中测试逻辑(根据实际业务实现)
    private hitTest(x: number, y: number): string | null {
        // 遍历当前页面的可获焦组件列表,判断坐标是否在其边界内
        // 返回命中的组件 ID
        return null; 
    }
}

三、 高阶实战:基于注视时长的“防误触”与“确认”机制

场景:在人眼自然扫视时,视线会频繁划过多个 UI 元素。为防止焦点疯狂跳动,需引入“注视时长(Dwell Time)”机制,只有当视线在目标上停留超过设定阈值(如 500ms)时,才真正触发焦点转移或点击操作。

export class DwellFocusManager {
    private dwellThreshold: number = 500; // 注视确认阈值(毫秒)
    private currentTarget: string | null = null;
    private dwellTimer: number = -1;

    onGazeUpdate(targetId: string | null) {
        // 视线离开当前目标,重置计时
        if (targetId !== this.currentTarget) {
            clearTimeout(this.dwellTimer);
            this.currentTarget = targetId;
            
            // 视线命中新目标,开始计时
            if (targetId) {
                this.dwellTimer = setTimeout(() => {
                    // 达到阈值,确认选中并转移焦点
                    this.focusController.requestFocus(targetId);
                    this.triggerClick(targetId); 
                }, this.dwellThreshold);
            }
        }
    }
}

四、 架构升级:从“单点映射”到“多模态自适应引擎”

在真实的复杂交互场景中,眼动数据存在生理噪声、设备位移和焦点竞争。我们需要构建一个具备自我纠错能力的引擎。

  • 多模态意图融合(Multimodal Fusion):视线仅作为“候选焦点(Gaze Candidate)”,必须结合头部姿态(Head Pose)、语音指令或微手势(如捏合)进行“多模态确认(Multimodal Confirmation)”,彻底解决“Midas Touch(迈达斯触摸)”误触问题。
  • 动态自适应校准(Adaptive Calibration):在用户自然交互过程中,利用用户的“主动点击”作为隐式反馈(Implicit Feedback),动态修正眼动仪的偏移矩阵,实现无感校准。
  • 卡尔曼滤波与防抖(Kalman Filtering):引入信号处理算法,对原始高频眼动数据进行平滑,消除眼球微颤(Micro-saccades)带来的焦点高频闪烁。

五、 高阶实战:企业级视线焦点控制引擎(GazeFocusEngine)

import { hilog } from '@kit.PerformanceAnalysisKit';
import { UIContext } from '@kit.ArkUI';

// 1. 强类型约束:多模态状态机与焦点候选
interface GazeCandidate {
    componentId: string;
    gazePoint: { x: number; y: number };
    enterTimestamp: number;
    confidence: number; // 视线停留置信度
}

enum FocusState {
    IDLE,
    HOVERING,      // 视线悬停
    CONFIRMING,    // 多模态确认中
    FOCUSED        // 焦点锁定
}

// 2. 核心引擎:融合滤波、防抖与系统级焦点管理
export class GazeFocusEngine {
    private static readonly TAG = 'GazeFocusEngine';
    private state: FocusState = FocusState.IDLE;
    private candidate: GazeCandidate | null = null;
    private uiContext: UIContext;

    constructor(uiContext: UIContext) {
        this.uiContext = uiContext;
    }

    /**
     * 高阶处理:融合卡尔曼滤波与驻留确认
     */
    processGazeData(rawPoint: { x: number; y: number }) {
        // 1. 信号平滑:消除眼球微颤(此处为滤波抽象,实际需接入卡尔曼滤波器)
        const smoothedPoint = SignalProcessor.kalmanFilter(rawPoint);
        
        // 2. 命中测试与状态机流转
        const hitId = this.hitTest(smoothedPoint);
        
        if (hitId && hitId === this.candidate?.componentId) {
            // 视线持续停留在同一组件,更新驻留时间
            const dwellTime = Date.now() - this.candidate.enterTimestamp;
            
            if (dwellTime >= 500 && this.state === FocusState.HOVERING) {
                // 达到驻留阈值,进入确认状态(等待多模态确认或自动确认)
                this.state = FocusState.CONFIRMING;
                this.executeFocusTransition(hitId);
            }
        } else if (hitId) {
            // 视线转移到新组件,重置状态机
            this.state = FocusState.HOVERING;
            this.candidate = { 
                componentId: hitId, 
                gazePoint: smoothedPoint, 
                enterTimestamp: Date.now(), 
                confidence: 1.0 
            };
        } else {
            // 视线离开所有可获焦区域
            this.state = FocusState.IDLE;
            this.candidate = null;
        }
    }

    /**
     * 高阶执行:系统级焦点接管与无障碍播报
     */
    private executeFocusTransition(targetId: string) {
        const focusController = this.uiContext.getFocusController();
        
        try {
            // 1. 强制请求焦点,并处理焦点竞争
            focusController.requestFocus(targetId);
            this.state = FocusState.FOCUSED;
            
            // 2. 联动无障碍服务(Accessibility),为视障用户提供语音播报
            this.uiContext.getAccessibilityManager().announce(`已聚焦至 ${targetId}`);
            
            hilog.info(0x0001, GazeFocusEngine.TAG, `[Focus Engine] 焦点已转移至: ${targetId}`);
        } catch (err) {
            hilog.error(0x0001, GazeFocusEngine.TAG, `焦点转移失败: ${(err as Error).message}`);
        }
    }
}

六、 复杂度提升点解析

  1. 状态机驱动的焦点流转:摒弃了简单的 if-else 触发,引入了 FocusState 状态机(IDLE -> HOVERING -> CONFIRMING -> FOCUSED)。这是处理复杂时序交互和防误触的标准工程范式。
  2. 信号处理与防抖抽象:显式引入了 SignalProcessor.kalmanFilter,体现了对硬件物理特性的深刻理解。真实的眼动数据必须经过滤波才能用于 UI 交互。
  3. 系统级 UIContext 接管:通过 uiContext.getFocusController() 和 getAccessibilityManager() 进行焦点控制与无障碍联动,而非组件级的局部操作,确保了全局焦点的唯一性和系统的可访问性。
  4. 隐式反馈与自适应校准预留:架构设计中预留了 confidence 字段和状态流转,为后续接入“用户主动点击修正眼动偏移”的自适应校准算法打下了完美的数据基础。

七、 工程级避坑与高阶实践

  1. 焦点竞争与抢占策略:在多窗口或弹窗场景下,视线焦点极易与系统默认焦点冲突。必须在 GazeFocusEngine 中维护一个“焦点优先级队列”,当系统级弹窗出现时,自动挂起(Suspend)视线追踪引擎。
  2. 性能红线与帧率保障:眼动数据的采样率通常高达 60Hz-120Hz。processGazeData 中的 hitTest 必须采用空间索引(如四叉树)或限制遍历深度,严禁在每一帧都进行全量 DOM 树遍历,否则会导致严重的 UI 掉帧。
  3. 隐私合规与传感器权限:眼动数据属于高度敏感的生物识别信息。必须在 module.json5 中声明 ohos.permission.CAMERA 及相关的生物特征权限,并在 UI 上提供显眼的“眼动追踪已开启”状态指示器,允许用户一键物理/软件关闭。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐