Ext Button的onClick和handler

本文详细解析了EXT中Button点击事件的两种实现方式:使用onClick方法与handler配置项的区别。通过对源码的深入分析,解释了这两种方式的本质不同,并推荐了最佳实践。

在EXT中,当我们要为按钮点击添加处理function的时候,可以看到一般人的实现分成2类:

1.使用onClick: function xx()

2.使用handler: function xx()

 

完成后,我们会发现,无论用哪一种实现,再点击按钮时都能触发xx这个function. 那么有人就会考虑,为什么EXT提供了2个功能一样的东西,或者说这2种方式有哪些细微的不同?

 

首先有一点需要明确,在Button中,onClick是一个方法,而handler是一个配置项

接着,为了明确这2种方式本质上的区别,我们查看Button的源码:

 

 

// private
    onClick : function(e){
            ......一系列其他无关的代码
            if(this.handler){
                //this.el.removeClass('x-btn-over');
                this.handler.call(this.scope || this, this, e);
            }
        }
    },

 

源码中可以看到,handler在onClick的实现中被调用。进一步分析,我们点击按钮的时候,又是如何会调用onClick的?看下面一段源码:

 

// private
    initButtonEl : function(btn, btnEl){
       ......一系列无关的代码
        if(this.repeat){
           ......一系列无关的代码
            this.mon(repeater, 'click', this.onRepeatClick, this);
        }else{
            this.mon(btn, this.clickEvent, this.onClick, this);
        }
    },

在初始化button的el的时候,Ext通过this.mon将 'click'事件和onClick绑定在了一起。(注:这里mon方法是Ext3.x中对on方法的升级版,为了防止内存泄漏之类的)。

 

综上,整个流程便是:  Button实例化——> 'click'事件 ——>this.onClick——>this.handler

因此,我们配置了handler,在按钮点击的时候,自然会被触发。然而另一种方式写了onClick之后哦,this.handler会失效。onClick的方式是对EXT源码的重写和覆盖,而不是调用,会破坏EXT按钮中原有的逻辑。

同时可以注意到,onClick在源码中是被标注为//private的,API中也查不到这个方法。所以在实现按钮的点击事件的时候,我们应该使用handler这个配置项,而不是重写onClick方法。

你遇到的这个错误: ``` Uncaught (in promise) Error: [webext-bridge] No handler registered in 'popup' to accept messages with id 'send-current-language' at f (index.global.js:20:29051) ... ``` ### ❌ **你的核心功能无关!** 这是一个**浏览器扩展(如翻译插件、广告拦截器等)注入的脚本错误**,它来自某个使用了 `webext-bridge` 的 Chrome 扩展(很可能是「沉浸式翻译」或类似工具)。 ✅ **它不会影响你的鱼群模拟逻辑,也不是你的代码问题。** --- ## ✅ 真正的问题:**点击按钮无法生成固定障碍物** 我们来重点排查这个问题。 --- ### 🔍 问题定位 你说: > “当前已经实现自动躲避活动小圆点,但是点击按钮不能生成固定障碍物” 说明: - 白球移动 ✔️ - 鱼群避让白球 ✔️ - 按钮按下 → 没有在当前位置创建障碍物 ❌ 所以我们聚焦在 **按钮事件处理逻辑** 上。 --- ### 🧪 关键代码段分析 ```js if (btn === 1) { if (useCursorMode) { // 只有在非菜单模式下才允许放置 if (py > HEADER_HEIGHT && py < FOOTER_Y) { OBSTACLES.push({ x: px, y: py }); } } else { // 菜单模式:执行按钮功能 if (!isNavigating && focusedIndex >= 0) { isNavigating = true; const action = CONTROLS[focusedIndex]; pressedButton = action; setTimeout(() => { pressedButton = null; }, 150); setTimeout(() => { action.onClick(); }, 100); setTimeout(() => { isNavigating = false; }, 1000); } } } ``` ### ✅ 正确行为应该是: | 条件 | 行为 | |------|------| | 白球不在底部导航区(`py < FOOTER_Y`)且 `btn===1` → 放置障碍物 | ✅ 应该触发 | | 白球进入底部区域 → 自动切换到菜单选择模式 | ✅ | | 在菜单模式中按按钮 → 触发对应功能(清除/返回) | ✅ | --- ## ❗ 问题出在哪? 你可能遇到了以下两种情况之一: --- ### ❌ 错误 1:`btn === 1` 根本没有被正确接收到 #### 原因: WebSocket 发来的 `data.btn` 不是数字 `1`,而是字符串 `"1"` 或布尔值 `true` #### ✅ 修复方法:改为宽松判断 ```js // ❌ 错误写法 if (btn === 1) // ✅ 正确写法 if (btn == 1 || btn === true || btn === "1") ``` 或者更安全地转为布尔值: ```js const buttonPressed = Boolean(btn); ``` --- ### ✅ 修改建议(立即生效) 找到这段代码: ```js if (btn === 1) { ``` 替换成: ```js if (btn == 1 || btn === true || btn === "1") { ``` 完整修改如下: ```js // ✅ 安全检测按钮是否按下 const isButtonPressed = (btn == 1 || btn === true || btn === "1"); if (isButtonPressed) { if (useCursorMode) { // 当前是自由模式:放置障碍物 if (player.pos.y > HEADER_HEIGHT && player.pos.y < FOOTER_Y) { OBSTACLES.push({ x: player.pos.x, y: player.pos.y }); console.log("🎯 固定障碍物已放置:", { x: player.pos.x, y: player.pos.y }); } } else { // 菜单模式:执行按钮命令 if (!isNavigating && focusedIndex >= 0) { isNavigating = true; const action = CONTROLS[focusedIndex]; pressedButton = action; setTimeout(() => { pressedButton = null; }, 150); setTimeout(() => { action.onClick(); }, 100); setTimeout(() => { isNavigating = false; }, 1000); } } } ``` 同时加一个日志输出,方便调试: ```js OBSTACLES.push({ x: player.pos.x, y: player.pos.y }); console.log("✅ 成功添加障碍物:", OBSTACLES[OBSTACLES.length - 1]); ``` --- ### ❌ 错误 2:摇杆服务没发 `btn` 字段 请确认你的 WebSocket 后端发送的数据结构是: ```json { "type": "joystick", "x": 512, "y": 512, "btn": 1 } ``` 而不是: ```json { "btn": true } // 缺少 type 坐标 ``` 或漏掉了 `btn` 字段。 🔧 **解决方案**: 在 `ws.onmessage` 中打印原始数据: ```js ws.onmessage = (event) => { try { const data = JSON.parse(event.data); console.log("📡 收到消息:", data); // 👈 加这行看真实数据 if (data.type !== 'joystick') return; applyJoystickForce(data.x, data.y, data.btn); } catch (e) { console.error('📦 解析失败:', e); } }; ``` 运行后打开浏览器控制台(F12),观察是否真的收到了 `btn: 1`。 --- ### ✅ 最终修复版片段(关键部分) ```js function applyJoystickForce(x, y, rawBtn) { const dx = x - 512; const dy = -(y - 512); const magnitude = Math.sqrt(dx * dx + dy * dy); const MAX_MAG = 512; const DEAD_ZONE = 60; // 控制玩家移动 if (magnitude > DEAD_ZONE) { const fx = (dx / magnitude) * player.thrust * (magnitude / MAX_MAG); const fy = (dy / magnitude) * player.thrust * (magnitude / MAX_MAG); player.acc = { x: fx, y: fy }; } else { player.acc = { x: 0, y: 0 }; } const px = player.pos.x; const py = player.pos.y; const isInNavZone = py >= NAVIGATION_TRIGGER_ZONE; if (isInNavZone) { if (useCursorMode) { useCursorMode = false; focusedIndex = px < LOGICAL_WIDTH / 2 ? 0 : 1; } } else { if (!useCursorMode) { useCursorMode = true; } } // ✅ 安全判断按钮是否按下 const isButtonPressed = (rawBtn == 1 || rawBtn === true || rawBtn === "1"); if (isButtonPressed) { if (useCursorMode) { // ✅ 放置固定障碍物 if (py > HEADER_HEIGHT && py < FOOTER_Y) { OBSTACLES.push({ x: px, y: py }); console.log("🎯 已放置固定障碍物 @", { x: px, y: py }); } } else { // 执行菜单操作 if (!isNavigating && focusedIndex >= 0) { isNavigating = true; const action = CONTROLS[focusedIndex]; pressedButton = action; setTimeout(() => { pressedButton = null; }, 150); setTimeout(() => { action.onClick(); }, 100); setTimeout(() => { isNavigating = false; }, 1000); } } } } ``` --- ### ✅ 测试步骤(你必须做的三件事) 1. 打开浏览器开发者工具(F12) 2. 查看 Console 是否打印 `"📡 收到消息"` 并看到 `btn` 字段 3. 移动白球到中间区域,按下按钮,查看是否打印 `"🎯 已放置固定障碍物"` 4. 如果没打印 → 是硬件/通信问题 5. 如果打印了但没显示 → 检查绘图循环是否绘制了 `OBSTACLES` --- ### ✅ 再检查一次绘图逻辑 确保你在主循环中有这段代码: ```js // 绘制所有固定障碍物 ctx.fillStyle = '#000'; ctx.strokeStyle = '#333'; ctx.lineWidth = 4; for (let obs of OBSTACLES) { ctx.beginPath(); ctx.arc(obs.x, obs.y, OBSTACLE_RADIUS, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); } ``` 并且 `OBSTACLE_RADIUS = 50`,别太小看不见。 --- ## ✅ 总结:你现在应该怎么做? | 步骤 | 操作 | |------|------| | 1️⃣ | 在 `applyJoystickForce` 上方打印 `data` 看是否收到 `btn` | | 2️⃣ | 把 `btn === 1` 改成 `btn == 1` 或 `Boolean(btn)` | | 3️⃣ | 添加 `console.log("🎯 放置")` 确认逻辑进入 | | 4️⃣ | 确保障碍物绘制代码存在且半径够大 | | 5️⃣ | 移动白球离开底部再按按钮测试 | --- ### ✅ 附加建议:可视化反馈 你可以让新放置的障碍物短暂变红一下,表示“刚放”: ```js OBSTACLES.push({ x: px, y: py, temp: true, life: 30 }); // 增加临时标记 ``` 然后在绘制时: ```js for (let obs of OBSTACLES) { ctx.beginPath(); ctx.arc(obs.x, obs.y, OBSTACLE_RADIUS, 0, Math.PI * 2); ctx.fillStyle = obs.temp ? '#f33' : '#000'; // 刚放的是红色 ctx.fill(); ctx.strokeStyle = '#333'; ctx.lineWidth = 4; ctx.stroke(); if (obs.temp) { obs.life--; if (obs.life <= 0) obs.temp = false; } } ``` 并在 `animate()` 开头更新状态。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值