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);
  }

}
相关推荐
tedcloud1234 小时前
UI-TARS-desktop部署教程:构建AI桌面自动化系统
服务器·前端·人工智能·ui·自动化·github
UXbot7 小时前
AI原型设计工具如何支持团队协作与快速迭代
前端·交互·个人开发·ai编程·原型模式
ZC跨境爬虫8 小时前
跟着MDN学HTML_day_48:(Node接口)
前端·javascript·ui·html·音视频
PieroPc9 小时前
CAMWATCH — 局域网摄像头监控系统 Fastapi + html
前端·python·html·fastapi·监控
巴巴博一10 小时前
2026 最新:Trae / Cursor 一键接入 taste-skill 完整教程(让 AI 前端告别“AI 味”)
前端·ai·ai编程
kyriewen11 小时前
半夜三点线上崩了,AI替我背了锅——用AI排错,五分钟定位三年老bug
前端·javascript·ai编程
kyriewen11 小时前
我让 AI 当了 24 小时全年无休的“毒舌考官”
前端·ci/cd·ai编程
hexu_blog11 小时前
vue+java实现图片批量压缩
java·前端·vue.js
IT_陈寒11 小时前
为什么你应该学习JavaScript?
前端·人工智能·后端