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

}
相关推荐
释怀不想释怀1 小时前
Ajax,vue生命周期(自动加载页面发出请求)Axios
前端·javascript·ajax
一点晖光1 小时前
ios底部按钮被挡住
前端·ios·微信小程序
cz追天之路7 小时前
华为机考--- 字符串最后一个单词的长度
javascript·css·华为·less
Light607 小时前
CSS逻辑革命:原生if()函数如何重塑我们的样式编写思维
前端·css·响应式设计·组件化开发·css if函数·声明式ui·现代css
蜡笔小嘟8 小时前
宝塔安装dify,更新最新版本--代码版
前端·ai编程·dify
ModyQyW8 小时前
HBuilderX 4.87 无法正常读取 macOS 环境配置的解决方案
前端·uni-app
bitbitDown9 小时前
我的2025年终总结
前端
五颜六色的黑9 小时前
vue3+elementPlus实现循环列表内容超出时展开收起功能
前端·javascript·vue.js
wscats10 小时前
Markdown 编辑器技术调研
前端·人工智能·markdown
EnoYao10 小时前
Markdown 编辑器技术调研
前端·javascript·人工智能