收藏
回答

小游戏canvas开发,两个页面,可以点击切换页面。监听点击事件的时候,点击事件被多次执行?

框架类型 问题类型 API/组件名称 终端类型 微信版本 基础库版本
小游戏 Bug 事件监听 工具 微信工具 1.06.2303220

这几天在封装canvas,想弄成比较好开发的自用框架。

其实就是两个页面,点一下开始游戏,进入游戏,点一下结束,退出游戏,回到开始页面。(说白了,就是事件监听和页面切换)

我在第一次点击的时候,一切正常,正常跳转到了游戏主页面。我点退出的时候,也正常,回到了主页。

然后我多点了几次,忽然发现,日志打印了3次,也就是说触发了3次,接着8次,21次,离了个大谱,点了几次之后,100多次了。再点几次估计能上千。

然后就这么简单两个按钮跳转,直接让页面卡死。

底下的console里能看到,后面多点击一下,打印的日志乌拉拉几何级上升。

代码结构如下,就用红框的五个文件:

game.js里的直接调用new  test(),会在test.js和test2.js来回跳转,模拟两个页面。

我上传一下完整代码吧:

test.js

import  buttonSprite     from './base/buttonSprite'
import  Point2d     from './base/point2d'
import  test2Page   from './test2'



let ctx   = canvas.getContext('2d')


let gamesPage =0;



//精灵摆放
const arr = [
    {x:100y:390width:100height:50,name:'开始',color:'green',image:"images/start.png",funName:"start"},//退出
 ]


 let i=0;
 let limit = 1;
export default class test{


    constructor() {
            document.body.innerHTML="";//清空所有内容
            // this.init();//数据初始化
            console.log("进入开始页面!-----")
            // console.log("数组:"+this.allShapes);
            this.draw()
        }
        
    
      start(){
        let test2 = new test2Page();
      }

      draw(){
          ctx.fillStyle = "#f5f5f5";  
        // ctx.fillStyle = "#000000";  
         ctx.fillRect(00, canvas.width, canvas.height);  
    
         let i = 0;
         let startButton = new buttonSprite(arr[i].x,arr[i].y,arr[i].width,arr[i].height,arr[i].name,arr[i].funName);
         startButton.drawToCanvas(ctx,arr[i].image);
         
         this.allShapes=[];
         this.allShapes.push(startButton);
         canvas.addEventListener('touchend'this.handleTestEvent('touchend'),false)//目前一个canvas就监听所有按钮了
        
      }
      
      getNewEvent(event) {
          let touchX = event.changedTouches[0].clientX;
          let touchY = event.changedTouches[0].clientY;
          let point = new Point2d(touchX, touchY)    
          return {      point,      isStopBubblefalse,      ...event,    }  
      }

      handleTestEvent = (name) => (event) => {
            // event.stopPropagation(); 
           event = this.getNewEvent(event)   
           this.allShapes.forEach((shape) => { // 获取当前事件的所有监听者 ,遍历每个形状   
            //    const listerns = shape.listenerMap.get(name)  
              if (        
                shape.isPointInClosedRegion(event.point.x,event.point.y)  //判断点击落在对应的图形内,则触发事件     
                && (!event.isStopBubble)      
                  ) 
              {       
                console.log("命中shape:"+shape)
                this.callMethodOnFunctionName(shape.functionName,[1]);//执行对应函数
              }
                
            }) 
        }    

  //挨个判断是什么函数……暂时没法写成反射
  callMethodOnFunctionName(functionName,arg) {  
    // 使用 Function 构造函数创建一个新的函数  
    let newFunction = new Function(functionName); 
    switch(functionName){
      case "start":
        this.start();
        break;
      case "showList":
        this.showList(arg);
        break;
      case "clearStor":
        this.clearStor(arg);
        break;
    }
  }  

}


test2.js

import  buttonSprite     from './base/buttonSprite'
import  Point2d     from './base/point2d'
import  testPage   from './test'

let ctx   = canvas.getContext('2d')

//精灵摆放
const arr = [
    {x:200y:90width:100height:50,name:'退出',color:'green',image:"images/exit.png",funName:"exit"},//退出
    //{x:100, y:200, width:100, height:50,name:'开始游戏',color:'green',image:"images/startBtn2.png",funName:"startGame"},//开始
    // {x:100, y:260, width:100, height:50,name:'排行榜',color:'blue',image:"images/paihangbang.png",funName:"showList"},//排行榜
    // {x:100, y:320, width:100, height:50,name:'清除缓存',color:'red',image:"images/clearStorage.png",funName:"clearStor"},//清除缓存
 ]


export default class test{


    
    init(){
        // this.i = 0;
        //精灵摆放
        this.arr = [
            {x:200y:90width:100height:50,name:'退出',color:'green',image:"images/exit.png",funName:"exit"},//退出
        ]
        // this.gamesPage =0;
    }


    constructor() {

        document.body.innerHTML="";//清空所有内容
            this.draw()
      }
    
      exit(){
        let test = new testPage(0);

      }

      draw(){
          ctx.fillStyle = "#f5f5f5";  
        // ctx.fillStyle = "#000000";  
         ctx.fillRect(00, canvas.width, canvas.height);  
    
         let i = 0;
         let exitButton = new buttonSprite(arr[i].x,arr[i].y,arr[i].width,arr[i].height,arr[i].name,arr[i].funName);
         exitButton.drawToCanvas(ctx,arr[i].image);
        
         this.allShapes=[];
        //  if()
         this.allShapes.push(exitButton);
         canvas.addEventListener('touchend'this.handleEvent('touchend'),true)//目前一个canvas就监听所有按钮了
      }

      getNewEvent(event) {
        let touchX = event.changedTouches[0].clientX;
        let touchY = event.changedTouches[0].clientY;
        let point = new Point2d(touchX, touchY)    
        return {      point,      isStopBubblefalse,      ...event,    }  
    }

    handleEvent = (name) => (event) => { 
        // event.stopPropagation();   
        event = this.getNewEvent(event)   
        this.allShapes.forEach((shape) => { // 获取当前事件的所有监听者 ,遍历每个形状   
         //    const listerns = shape.listenerMap.get(name)     
           if (        
             shape.isPointInClosedRegion(event.point.x,event.point.y)       
             && (!event.isStopBubble)      
               ) 
           {       
             console.log("命中shape"+shape)
             this.callMethodOnFunctionName(shape.functionName,[1]);//执行对应函数
           }
             
         }) 
     }    
  
  //挨个判断是什么函数……暂时没写成反射
  callMethodOnFunctionName(functionName,arg) {  
    // 使用 Function 构造函数创建一个新的函数  
    let newFunction = new Function(functionName); 
    switch(functionName){
      case "exit":
        this.exit();
        break;
      case "showList":
        this.showList(arg);
        break;
      case "clearStor":
        this.clearStor(arg);
        break;
    }
  }  
  
}


base文件夹下的buttonSprite.js

let x = 0;
let y = 0;
let width = 0;
let height = 0;
let name="按钮";
let leftTop={x:0,y:0};

/**
 * 游戏基础的精灵类
 */
export default class buttonSprite {
  
  constructor( x, y, width, height,name,functionName) {
  
    this.name = name
    this.functionName = functionName

    this.x = x
    this.y = y
    this.leftTop = { x, y } 
    leftTop  = { x, y } 

    this.width  = width
    this.height = height
    this.visible = true
    this.listenerMap = new Map()
    this.props = {leftTop, width,height}

  }

  on(eventName, listener) {
    console.log("hey,wobeidiaoyongle,jingguolew")
    if (this.listenerMap.has(eventName)) {
      this.listenerMap.get(eventName).push(listener)
    } else {
      this.listenerMap.set(eventName, [listener])
    }
  }


  /**
   * 将精灵图绘制在canvas上
   */
  drawToCanvas(ctx,imgSrc) {
 
    if ( !this.visible )
      return
     
      let img = new Image();
     
      let x = this.x;
      let y = this.y;
      let width = this.width; 
      let height = this.height;
      img.onload = function(){
        ctx.drawImage(
          img,
          x,
          y,
          width,
          height
        )}
        img.src = imgSrc;//先onload再给出src
    }
        



  callMethodOnFunctionName() {  
    // 使用 Function 构造函数创建一个新的函数  
    const newFunction = new Function(this.functionName);  
    
    // 使用 apply 方法调用传递的函数  
    newFunction.apply(this, [this]);  
  }  


    // 判断鼠标的点是否在图形内部  
    isPointInClosedRegion(touchX,touchY) {    
      // const { x, y } = mouse.point    
      const { leftTop, width, height } = this.props    
      const { x: minX, y: minY } = leftTop    
      const maxX = minX + width    
      const maxY = minY + height    
      if (touchX >= minX && touchX <= maxX && touchY >= minY && touchY <= maxY) {      
        return true    
      }    
      return false  
    }


  }


base 文件夹下的point2d.js

export default class point2d {

    constructor(x,y) {
        this.x = x || 0;
        this.y = y || 0;
    }
    
    clone() {
        return this.constructor(this.x, this.y);
    }
    
    add(v) {
        this.x += v.x;
        this.y += v.y
        return this;
    }
    
   random() {
        this.x = Math.random() *1800;
        this.y = Math.random() * 800;
        return this
    }
    
}



入口文件:game.js

import './js/libs/weapp-adapter'
import './js/libs/symbol'


import test from './js/test'

new  test()


回答关注问题邀请回答
收藏

1 个回答

  • sweetie
    sweetie
    06-26

    退出后按钮没销毁(回收),再进入后又重新生成了新的按钮

    06-26
    有用
    回复
登录 后发表内容