import { BehaviorCatcherConfig, ReporterContext } from '../types'; import { BehaviorCatcher, FiniteStateMachine } from './common'; enum HoverState { ENTER, LEAVE, } class HoverFiniteStateMachine extends FiniteStateMachine { constructor() { super(); this.init([HoverState.ENTER, HoverState.LEAVE], HoverState.LEAVE); } } export interface HoverTargetConfig extends BehaviorCatcherConfig { context: ReporterContext; } // FIXME: 目前 HoverTarget 使用 mouseenter/mouseleave 判定 hover 在部分浏览器实现中是不置信的,比如 Mac 微信内嵌 webview 中,mouseenter 并不总能被一个对应的 mouseleave 回收(页面失焦之类的情况无法触发) export class HoverTarget extends BehaviorCatcher { private context: ReporterContext; private enterTime = 0; private finiteStateMachine = new HoverFiniteStateMachine(); private mouseEnter: (event: MouseEvent) => void; private mouseLeave: (event: MouseEvent) => void; constructor(config: HoverTargetConfig) { super(config); this.context = config.context; this.mouseEnter = this.privateMouseEnter.bind(this); this.mouseLeave = this.privateMouseLeave.bind(this); this.listen(); } listen() { this.el.addEventListener('mouseenter', this.mouseEnter); this.el.addEventListener('mouseleave', this.mouseLeave); } updateConfig() {} destroy() { this.el.removeEventListener('mouseenter', this.mouseEnter); this.el.removeEventListener('mouseleave', this.mouseLeave); } private privateMouseEnter() { if (this.finiteStateMachine.getCurrent() === HoverState.ENTER) { this.finiteStateMachine.switchTo(HoverState.LEAVE); // todo: 报错需要更加详细 throw new Error(`${this.key} hover catcher 状态不对`); } this.finiteStateMachine.switchTo(HoverState.ENTER); this.enterTime = Date.now(); } private privateMouseLeave() { if (this.finiteStateMachine.getCurrent() === HoverState.LEAVE) { throw new Error(`${this.key} hover catcher 状态不对`); } if (this.enterTime === 0) throw new Error('enterTime 不能为 0'); this.context.reporter?.reportElementHover({ key: this.key, allTime: Date.now() - this.enterTime, }); this.finiteStateMachine.switchTo(HoverState.LEAVE); this.enterTime = 0; } }