从 PIXI.js 中的同级或父 DOM 元素捕获鼠标事件

Posted

技术标签:

【中文标题】从 PIXI.js 中的同级或父 DOM 元素捕获鼠标事件【英文标题】:Capture mouse events from sibling or parent DOM element in PIXI.js 【发布时间】:2018-05-15 02:17:32 【问题描述】:

我想在 两个 层上捕获鼠标事件:PIXI 的画布和覆盖 div。我有以下类型的 html 设置,其中div.overlay 高于canvas.pixi

<div class="parent">
  <canvas class="pixi"></canvas>
  <div class="overlay"></div>
</div>

canvas 位于顶部时,PIXI 交互工作正常,但当画布被div.overlay 覆盖时,我无法捕获任何事件。

我发现setTargetElement 似乎可以让我们为捕获元素定义 DOM 元素,我尝试像这样使用它:

const renderer = PIXI.autoDetectRenderer(...);
renderer.plugins.interaction.setTargetElement(document.querySelector('.overlay'));

使用这种技术,我能够捕获mousemove 事件,但不幸的是clickmousedown 等不起作用。

我还尝试复制在div.overlay 上捕获的原始事件并复制并在canvas 上调度事件,如下所示,但这也不起作用。

document.querySelector('.overlay').addEventListener('mousedown', (e) => 
  const eventCopy = document.createEvent('MouseEvents');
  eventCopy.initMouseEvent(
    e.type, e.bubbles, e.cancelable, e.view,
    e.detail, e.pageX || e.layerX,
    e.pageY || e.layerY, e.clientX,
    e.clientY, e.ctrlKey, e.altKey, e.shiftKey,
    e.metaKey, e.button, e.relatedTarget
  );

  document.querySelector('.pixi').dispatchEvent(eventCopy);
);

有什么方法可以在覆盖的 DOM 元素上捕获鼠标事件并将这些事件传递给 PIXI?

为什么?

我想与 PIXI 元素交互,同时能够利用 D3 的缩放和画笔功能,目前正在覆盖 div 上处理。

更新和代码示例

我设法转发事件,除点击事件外,所有事件都由 PIXI 注册。点击事件可以通过重新触发pointerdownpointerup 事件来手动触发。查看https://jsfiddle.net/v3chhhjk/1/

【问题讨论】:

【参考方案1】:

一种可能的解决方案是捕获父元素中的所有事件并将其遍历回子元素。像这样。这是为点击完成的,同样适用于 mousemove。

function traverseEventToChild(e)
	if(e.type="click")
  	const eventCopy = document.createEvent('MouseEvents');
  	eventCopy.initMouseEvent(
      e.type, e.bubbles, e.cancelable, e.view,
      e.detail, e.pageX || e.layerX,
      e.pageY || e.layerY, e.clientX,
      e.clientY, e.ctrlKey, e.altKey, e.shiftKey,
      e.metaKey, e.button, e.relatedTarget
  	);
    console.log('parent handler');
  	document.querySelector('.pixi').dispatchEvent(eventCopy);
  


function piximousemove(e)
	console.log('pixi click');
  e.stopPropagation();


function overlaymousemove(e)
	console.log('overlay click');

document.querySelector('.pixi').onclick = piximousemove;
document.querySelector('.overlay').onclick = overlaymousemove;
<div class="parent" id='parent' onclick="traverseEventToChild(event);">
  <canvas class="pixi" id='canvas'>pixi</canvas>
  <div class="overlay" id='overlay'>overlay</div>
</div>

【讨论】:

这适用于普通的 JS 和 div 元素,但您是否也可以使用 PIXI.js?我尝试了类似的方法,但最终不得不自己使用 mousedown 和 mouseup 来跟踪点击事件 @FLekschas 我没有用 PIXI.js 尝试过,但是如果 PIXI.js 遵循普通的 JS 标准,那么上面应该可以正常工作,除非 PIXI.js 在其处理程序中阻止事件传播。 嗯,我已经测试过了,但它不起作用,这就是为什么我问如何让它在 PIXI.js 中工作。无论如何,谢谢,我已经弄清楚了。【参考方案2】:

如果您只需要通过叠加层传递事件 - 使用 css pointer-events: none;。 更多细节在这里:https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events

【讨论】:

抱歉,我应该更清楚一点:我需要在两个层上捕获事件:叠加层和 PIXI。不幸的是,CSS 技巧在这里没有帮助。【参考方案3】:

问题在于 DOM 是分层的。 Here's an old issue about the exact same thing。事实证明,你不能简单地将事件传播给兄弟姐妹,只有父母和孩子。我建议将您的 canvas 放在 .overlay 中,然后您可以捕获它并在冒泡阶段捕获它,具体取决于您是要在画布之前还是之后处理它们。

我认为以下应该按预期工作。

HTML:

<div id="parent">
    <div id="overlay">
        <canvas></canvas>
    </div>
</div>

示例 JS:

(function() 
    let parent = document.getElementById('parent');
    let overlay = document.getElementById('overlay');
    let canvas = document.querySelector('canvas');

    parent.addEventListener('click', e => console.log('parent capture'), true);
    overlay.addEventListener('click', e => console.log('overlay capture'), true);
    canvas.addEventListener('click', e => console.log('canvas capture'), true);
    canvas.addEventListener('click', e => console.log('canvas bubble'), false);
    overlay.addEventListener('click', e => console.log('overlay bubble'), false);
    parent.addEventListener('click', e => console.log('parent bubble'), false);

)()

【讨论】:

不幸的是,这不是一个选项。我还发现我可以将事件转发给兄弟姐妹,这样实际上工作正常。查看我提供的更新代码示例。【参考方案4】:

通过克隆事件并从mousedownmouseup 事件重构click 事件,我使它适用于mouseovermouseoutmousedownmouseupclick 事件。从事件层克隆事件时,除了click 之外的所有事件都可以开箱即用,但不知何故,单击事件不会触发。

所以给出以下 HTML 结构:

<div id="parent">
  <canvas id="pixi"></canvas>
  <div id="overlay"></div>
</div>

我听了一堆这样的事件:

const overlay = document.getElementById('overlay');

overlay.addEventListener('pointerdown', forwardDown);
overlay.addEventListener('pointerup', forwardUp);
overlay.addEventListener('pointermove', forward);
overlay.addEventListener('pointerover', forward);
overlay.addEventListener('pointerout', forward);

然后转发/克隆事件:

const canvas = document.getElementById('pixi');
let mousedown = false;

const clone = e => new e.constructor(e.type, e);
const forward = (e) =>  canvas.dispatchEvent(clone(e)); ;
const forwardDown = (e) =>  mousedown = true; forward(e); ;
const forwardUp = (e) => 
  forward(e);
  if (mousedown) console.log('It\'s a click!');
  mousedown = false;
;

该事件也可以发送到其他地方,例如 D3 可以识别。

演示:https://jsfiddle.net/v3chhhjk/3/

左上角演示了使用 PIXI 直接捕获事件时的工作原理。右上方显示克隆事件时的问题。左下角显示了一个奇怪的行为,重新触发相同的事件实际上使 PIXI 识别点击事件,最后是右下角。

【讨论】:

【参考方案5】:

适用于访问此页面,但希望 PIXI 成为覆盖层,位于仍可交互的底层之上。然后你仍然可以使用来自@F Lekschas 的示例,稍作修改。

在父节点上设置指针事件:无,然后在底层上设置指针事件:全部,然后将事件从底层转发到 PIXI 画布,如示例中所示。

我把头发拉到这个上面。我在将事件从父级转发到底层时遇到了各种问题,但是将事件从底层转发到 PIXI 画布就像一个魅力。

【讨论】:

以上是关于从 PIXI.js 中的同级或父 DOM 元素捕获鼠标事件的主要内容,如果未能解决你的问题,请参考以下文章

Pixi.js 中的自定义字体

基于 pixi.js 开发H5游戏黄金矿工

基于 pixi.js 开发H5游戏黄金矿工

Pixi.js 从动画 gif 文件生成 SpriteSheet Animation 或 MovieClip

jQuery添加删除DOM元素

Dom事件流冒泡捕获