弹窗关闭触发人物移动、弹窗关不掉问题 — 最终解决方案总结文档

一、问题现象

  • 点击弹窗【关闭按钮】,弹窗无法关闭或关闭极慢

  • 点击关闭的同时,底层角色会跑动、触发地面移动逻辑

  • 视觉上层弹窗置顶,但移动端依然穿透底层游戏画布

二、根本原因(核心!99% 同类Bug都是这个问题)

不是层级(z-index)问题,是移动端事件穿透 + 冒泡穿透

  1. Touch 事件不受 CSS 层级限制

    1. PC 端 z-index 高就能挡住点击

    2. 移动端 touchstart / touchmove 会穿透置顶DOM,直接触发底层游戏场景的移动事件

  2. 关闭按钮点击事件冒泡

    1. 点击按钮 → 冒泡到 game-scene → 触发地面移动

    2. 移动逻辑执行、状态冲突,导致弹窗变量卡住、关不掉

三、最终三层根治方案(缺一不可)

1. DOM 层:彻底阻止事件冒泡(@click.stop)

弹窗盒子 + 关闭按钮 双重 stop,截断所有向下穿透:

复制代码

<div v-if="showDialog" class="dialog-mask" @click.self="closeDialog"> <div class="dialog-box" @click.stop> <button @click.stop="closeDialog">关闭对话</button> </div> </div>

作用:点击弹窗内部完全不会传递到游戏层。

2. JS 逻辑层:弹窗打开时直接禁用所有地面移动

在所有场景触摸函数最开头加拦截:

复制代码

if(showDialog.value) return

修改函数:

  • handleSceneTouchStart

  • handleSceneTouchMove

  • handleSceneTouchEnd

作用:弹窗打开期间,底层移动逻辑彻底失效,杜绝状态冲突。

3. CSS 层:全局阻断底层交互(终极保险)

复制代码

.game-scene:has(.dialog-mask) { pointer-events: none; } .dialog-mask { pointer-events: auto; } .dialog-box { pointer-events: auto; }

作用:弹窗存在时,整个游戏场景禁止任何点击、触摸穿透。

四、修复后完整正确逻辑流程

  1. 点击角色 → 打开弹窗 showDialog = true

  2. 弹窗打开瞬间:CSS 禁用底层游戏交互 + JS 拦截触摸移动

  3. 点击关闭按钮:@click.stop 只执行 closeDialog

  4. 弹窗关闭后,游戏交互自动恢复

  5. 完全不会触发人物移动、无状态冲突、弹窗秒关

五、常见误区总结(踩坑记录)

  • ❌ 误区1:以为 z-index 不够高 → 层级不解决移动端touch穿透

  • ❌ 误区2:只给盒子加 stop,不给按钮加 → 依然穿透

  • ❌ 误区3:只改CSS不改JS → 依然有概率状态卡死

  • ✅ 正确:CSS + JS + DOM事件 三层拦截才是根治

六、适用场景(可复用通用方案)

所有 Vue + 移动端游戏 / PIXI / Canvas / 触摸场景 的弹窗穿透问题,均可直接套用此三层方案解决。