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

}
相关推荐
zhougl9961 小时前
html处理Base文件流
linux·前端·html
花花鱼1 小时前
node-modules-inspector 可视化node_modules
前端·javascript·vue.js
HBR666_1 小时前
marked库(高效将 Markdown 转换为 HTML 的利器)
前端·markdown
careybobo3 小时前
海康摄像头通过Web插件进行预览播放和控制
前端
TDengine (老段)3 小时前
TDengine 中的关联查询
大数据·javascript·网络·物联网·时序数据库·tdengine·iotdb
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
喝拿铁写前端4 小时前
字段聚类,到底有什么用?——从系统混乱到结构认知的第一步
前端
再学一点就睡4 小时前
大文件上传之切片上传以及开发全流程之前端篇
前端·javascript
木木黄木木6 小时前
html5炫酷图片悬停效果实现详解
前端·html·html5
请来次降维打击!!!6 小时前
优选算法系列(5.位运算)
java·前端·c++·算法