pixijs 适配 Apple Pencil

问题描述:

在Ipad上使用pixijs时,发现当使用Apple Pencil时,笔触事件会被触发两次。具体表现为,当使用Pencil点击某一元素时,其点击事件会被触发两次。

问题原因:

分析pixijs代码发现,其事件处理存在问题。

pixijs的事件处理(EventSystem.ts)代码结构如下:

kotlin 复制代码
public readonly supportsTouchEvents = 'ontouchstart' in globalThis;

public readonly supportsPointerEvents = !!globalThis.PointerEvent;

// 注册Pointer指针事件
if (this.supportsPointerEvents) {
  this.domElement.addEventListener('pointerdown', this.onPointerDown, true);
  this.domElement.addEventListener('pointerleave', this.onPointerOverOut, true);
  this.domElement.addEventListener('pointerover', this.onPointerOverOut, true);
} else {
  this.domElement.addEventListener('mousedown', this.onPointerDown, true);
  this.domElement.addEventListener('mouseout', this.onPointerOverOut, true);
  this.domElement.addEventListener('mouseover', this.onPointerOverOut, true);
}

// 注册Touche事件
if (this.supportsTouchEvents) {
  this.domElement.addEventListener('touchstart', this.onPointerDown, true);

  this.domElement.addEventListener('touchend', this.onPointerUp, true);
  this.domElement.addEventListener('touchmove', this.onPointerMove, true);
}

private onPointerDown(nativeEvent: MouseEvent | PointerEvent | TouchEvent): void {
     // 如果支持触摸事件,则过滤掉pointerType为'touch'的指针事件,只响应TouchEvent类型的事件
     if (this.supportsTouchEvents && (nativeEvent as PointerEvent).pointerType === 'touch') return;
     // ...
 }

private onPointerUp(nativeEvent: MouseEvent | PointerEvent | TouchEvent): void {
     if (this.supportsTouchEvents && (nativeEvent as PointerEvent).pointerType === 'touch') return;
     // ...
 }

private onPointerMove(nativeEvent: MouseEvent | PointerEvent | TouchEvent): void {
     if (this.supportsTouchEvents && (nativeEvent as PointerEvent).pointerType === 'touch') return;
     // ...
 }

如上面代码所示。由于Pad端同时支持Pointer指针事件和Touch指针事件。因此,事件(onPointerDownonPointerUponPointerMove)会分别被this.domElement.addEventListener('pointerXxx', this.onPointerXxx, true)this.domElement.addEventListener('touchXxx', this.onPointerXxx, true) 注册两次。

而为了避免事件响应两次,pixijs 使用if (this.supportsTouchEvents && (nativeEvent as PointerEvent).pointerType === 'touch') return;做了一次过滤,确保在浏览器支持Touch事件时,过滤掉pointerTypetouch的指针事件,只响应TouchEvent类型的事件。而问题在于,当使用Apple Pencil时,pointerType 的值为pen 。此时,事件无法被过滤,PointerEventTouchEvent 都会触发后续的事件响应。

解决方案:

针对pixijs的事件过滤原则,增加pointerTypepen 时的过滤条件。使用['touch', 'pen'].includes((nativeEvent as PointerEvent).pointerType) 替换(nativeEvent as PointerEvent).pointerType === 'touch' 同时过滤触摸和笔触事件。

详细修改方式参见:Fix: both touch and point events are called when using Apple penci 相关修复代码的PR已被官方合并,可通过升级pixijs的方式修复该问题。

如果不想升级,则可通过继承的方式重新注册事件,并采用强制重写相关私有函数的方式增加过滤:

kotlin 复制代码
// 重写了父类私有方法,添加@ts-ignore
// @ts-ignore
export class CustomManager extends InteractionManager {

  // 适配Apple Pencil,避免笔触事件触发两次
  // 重写onPointerUp、onPointerMove和onPointerDown事件
  // 由于这三个事件为私有事件,需要添加@ts-ignore忽略ts检查避免报错
  onPointerUp(originalEvent: InteractivePointerEvent): void {
    if (
      this.supportsTouchEvents &&
      ['touch', 'pen'].includes((originalEvent as PointerEvent).pointerType)
    ) {
      return;
    }
    // @ts-ignore
    super.onPointerUp(originalEvent);
  }

  onPointerMove(originalEvent: InteractivePointerEvent): void {
    if (
      this.supportsTouchEvents &&
      ['touch', 'pen'].includes((originalEvent as PointerEvent).pointerType)
    ) {
      return;
    }
    // @ts-ignore
    super.onPointerMove(originalEvent);
  }

  onPointerDown(originalEvent: InteractivePointerEvent): void {
    if (
      this.supportsTouchEvents &&
      ['touch', 'pen'].includes((originalEvent as PointerEvent).pointerType)
    ) {
      return;
    }
    // @ts-ignore
    super.onPointerDown(originalEvent);
  }

}
相关推荐
fthux5 小时前
RenoPit 能为普通业主做什么?看懂图纸、审查合同,提前发现装修坑
javascript·人工智能·ai·开源·github·chrome扩展·open source·edge扩展·firefox扩展
仿生狮子6 小时前
✂️ Nuxt 最简单的字体裁剪工具:Fontize
javascript·vue.js·nuxt.js
石小石Orz9 小时前
我发现了开发者AI产品营收的新方向
前端·虚拟现实
老马识途2.09 小时前
关于跨域问题的总结
java·前端
魔力女仆9 小时前
分享一个 JS 鼠标跟随贪吃蛇背景库
开发语言·javascript·计算机外设
别惊醒渔人11 小时前
Vue3 Diff 优化:最长递增子序列 LIS
前端·javascript·vue.js
颜酱11 小时前
07 | 把字段与指标同步到 Qdrant(生成阶段)
前端·人工智能·后端
用户0595401744611 小时前
把AI长期记忆测试从手动验证换成pytest,2天揪出11个隐藏Bug
前端·css
Larcher11 小时前
从“加载模型”界面到端侧推理:拆解一个 React + WebGPU 大模型 Demo
javascript·后端
Larcher12 小时前
从状态快照到惰性初始化:读懂 React useState 的三个关键场景
javascript·人工智能·后端