如何让PixiJS中交互容器移到指针下时触发mouseover事件?
问题
开发PixiJS 8.1.1游戏时碰到个交互问题:
- 原本逻辑是靠
mouseover事件记录选中的瓦片,点击后平台移动到选中的瓦片位置 - 但平台移动时,要是新的可点击瓦片滑到静止的鼠标指针下方,
mouseover事件不会自动触发 - 不想用“移动后手动把屏幕坐标转成瓦片坐标”的方案,想找更简便的触发方式
附示例代码:矩形悬停会变绿,点击后会滑向屏幕左侧,要是指针不动,矩形滑到指针下方时不会触发mouseover
const { Application, Container, Assets, Sprite, Graphics, RenderTexture, GraphicsContext } = PIXI; const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; let app, renderer, platform; const trailGraphics = new Graphics(); window.onload = async () => { renderer = await PIXI.autoDetectRenderer({}); const app = new Application(); await app.init({ background: '#9cc3ff', width: windowWidth, height: windowHeight, antialias: true, autoDensity: true, resolution: window.devicePixelRatio }); document.body.appendChild(app.canvas); const g = new Graphics() .beginFill(`#ff0000`) .drawRect(0, 0, 300, 200) .endFill(); const container = new Container(); container.interactive = true; container.buttonMode = true; container.x = 0; container.y = 0; container.addChild(g); app.stage.addChild(container); container.addListener(`mouseover`, function () { this.tint = `#00ff00`; }); container.addListener(`mouseout`, function () { this.tint = `#ff0000`; }); container.addListener(`click`, function () { container.x = 500; this.tint = `#ff0000`; }); app.ticker.add(time => { if (container.x > 0) { container.x -= 5; } }); };
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/8.1.1/pixi.min.js"></script>
解决方案
方法1:主动触发交互检测(推荐)
利用PixiJS内置的交互插件,在每帧或平台移动完成后,手动检测鼠标位置下的交互元素,然后触发对应的mouseover/mouseout事件,不用自己转坐标。
修改后的代码示例:
const { Application, Container, Graphics } = PIXI; const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; let app, currentHoveredElement = null; window.onload = async () => { const app = new Application(); await app.init({ background: '#9cc3ff', width: windowWidth, height: windowHeight, antialias: true, autoDensity: true, resolution: window.devicePixelRatio }); document.body.appendChild(app.canvas); const g = new Graphics() .beginFill(`#ff0000`) .drawRect(0, 0, 300, 200) .endFill(); const container = new Container(); container.interactive = true; container.buttonMode = true; container.x = 0; container.y = 0; container.addChild(g); app.stage.addChild(container); // 保留原事件监听 container.addListener(`mouseover`, function () { this.tint = `#00ff00`; currentHoveredElement = this; }); container.addListener(`mouseout`, function () { this.tint = `#ff0000`; currentHoveredElement = null; }); container.addListener(`click`, function () { container.x = 500; this.tint = `#ff0000`; currentHoveredElement = null; }); // 获取交互插件实例 const interaction = app.renderer.plugins.interaction; app.ticker.add(() => { if (container.x > 0) { container.x -= 5; } // 手动检测当前鼠标位置的交互元素 const mousePos = interaction.pointer.global; // 从舞台开始检测,拿到最顶层的交互元素 const hitElement = interaction.hitTest(mousePos, app.stage); // 对比当前悬停元素和检测到的元素,触发对应事件 if (hitElement !== currentHoveredElement) { if (currentHoveredElement) { currentHoveredElement.emit('mouseout'); } if (hitElement) { hitElement.emit('mouseover'); } } }); };
方法2:平台移动完成后一次性检测
如果平台是一次性移动到位(不是逐帧滑动),可以不用每帧检测,只在平台移动完成后执行一次检测逻辑,性能更好。
比如在判断平台停止移动时执行:
// 加入到ticker的回调中 if (container.x <= 0) { // 平台停止移动,执行一次检测 const mousePos = interaction.pointer.global; const hitElement = interaction.hitTest(mousePos, app.stage); if (hitElement) { hitElement.emit('mouseover'); } }
内容的提问来源于stack exchange,提问作者phil-daniels
相关产品推荐
相关产品推荐

