Pixi JS实现Mute静音切换按钮 点击切换状态与对应图标
PixiJS 静音切换按钮实现修正方案
原代码核心问题
- 逻辑混淆:误将按钮的可交互控制方法
enable()/disable()和静音业务状态绑定,这两个方法的作用是控制按钮是否响应点击,不是用来切换静音/非静音状态 - 纹理赋值错误:直接将图片路径字符串赋值给
Sprite.texture是无效的,Pixi需要使用预加载后存在纹理缓存中的PIXI.Texture对象 - 状态缺失:没有维护内部的静音状态标记,也没有绑定点击后切换状态、更新图标的逻辑
- 语法错误:
create()方法中设置back.pivot时引用了未定义的mute变量,会直接抛出运行时错误 - 事件逻辑断层:父类绑定的点击事件直接执行传入的
action回调,没有衔接按钮自身的状态切换流程,且事件回调未绑定this上下文,容易出现指向错误
前置要求
确保两个图标资源已经通过PIXI.Loader完成预加载,否则无法从缓存中读取到对应纹理。
修正后的代码
1. 基础ButtonView类微调
原父类不需要大改,只需要调整点击事件的触发逻辑,让子类可以在执行外部传入的action前先处理自身状态:
import { GlowFilter } from "pixi-filters"; import { Timeline } from "gsap/gsap-core"; export default class ButtonView extends PIXI.Container { constructor(options) { super() this.name = options.name || 'button' this.text = options.text || 'button' // 保存外部传入的回调 this.externalAction = options.action || this.noAction // 点击事件统一绑定到内部的onClick方法,方便子类重写扩展 this.on('click', this.onClick.bind(this)) this.on('tap', this.onClick.bind(this)) this.timeLine = new Timeline() this.outlineFilter = new GlowFilter({ outerStrength: 0, color: 0xffffff, distance: 20 }) this.filters = [this.outlineFilter] this.setInteractive(true) this.on('pointerup', this.pointerUp.bind(this)) this.on('pointerdown', this.pointerDown.bind(this)) this.on('pointerover', this.pointerOver.bind(this)) this.on('pointerout', this.pointerOut.bind(this)) } // 内部点击处理方法,子类可重写扩展 onClick() { this.externalAction() } setInteractive(bool) { this.interactive = bool this.buttonMode = bool } pointerUp() { this.scale.set(1) } pointerDown() { this.scale.set(0.95) } pointerOver() { this.timeLine.clear() this.timeLine.to(this.outlineFilter, .3, { outerStrength: 2 }) } pointerOut() { this.timeLine.clear() this.timeLine.to(this.outlineFilter, .3, { outerStrength: 0 }) } enable() { this.setInteractive(true) } disable() { this.setInteractive(false) } noAction() { console.log('ButtonView no action') } }
2. 修正后的MuteView静音按钮类
import ButtonView from "./ButtonView" export default class MuteView extends ButtonView { constructor(options) { super(options) // 初始化静音状态,默认非静音 this.isMuted = options.isMuted || false // 预存两个状态对应的纹理资源 this.textures = { mute: PIXI.Texture.from('./images/mute_icon.png'), unmute: PIXI.Texture.from('./images/unmute_icon.png') } this.header = new PIXI.Text('mute', { fill: 0xeeeeee, fontSize: 20 }) this.addChild(this.header) this.create() // 初始化时根据默认状态更新图标 this.updateIcon() } create() { const btnMute = PIXI.Sprite.from('./images/mute_box.png') btnMute.anchor.set(0.5) this.btnSprite = new PIXI.Sprite(this.textures.unmute) this.btnSprite.anchor.set(0.5) // 图标通过锚点居中放在按钮背景上,不需要手动计算坐标差 this.btnSprite.position.set(0, 0) // 修复原代码中未定义mute变量的错误 const back = new PIXI.Graphics() back.drawRect(-btnMute.width/2, -btnMute.height/2, btnMute.width, btnMute.height) this.btn = new PIXI.Container() this.btn.addChild(btnMute, this.btnSprite) this.addChild(back, this.btn) } // 重写父类点击方法,先切换自身状态再执行外部回调 onClick() { this.toggleMute() // 执行外部传入的回调,把当前静音状态传出去 this.externalAction(this.isMuted) } // 切换静音状态 toggleMute() { this.isMuted = !this.isMuted this.updateIcon() // 在这里添加实际的静音逻辑即可,比如: // Howler.mute(this.isMuted) 或者 audioElement.muted = this.isMuted } // 根据当前状态更新图标 updateIcon() { this.btnSprite.texture = this.isMuted ? this.textures.mute : this.textures.unmute } pointerUp() { this.btn.scale.set(1) } pointerDown() { this.btn.scale.set(0.95) } // 保留enable/disable原本控制交互的功能,和业务状态完全分离 enable() { super.enable() this.alpha = 1 } disable() { super.disable() this.alpha = 0.5 } }
使用方式
// 初始化静音按钮 const muteBtn = new MuteView({ name: 'muteBtn', isMuted: false, action: (isMuted) => { console.log('当前静音状态:', isMuted) // 此处编写全局静音的业务逻辑 } }) // 将按钮添加到舞台 app.stage.addChild(muteBtn)
内容的提问来源于stack exchange,提问作者Deni
相关产品推荐
相关产品推荐

