将 addEventListener 放在哪里,这样它就不会触发多次

Where to put addEventListener so it doesn't fire multiple times

我有一个 Javascript 文件,其中绘制了一个 canvas 圆圈。如果我点击一个圆圈,颜色必须改变。这行得通,但是 eventHandler 单击一次会触发多次。因此,该程序滞后很多。我该如何改进?

var cvs = document.getElementById("canvas");

var ctx = cvs.getContext("2d");

class Circle {
  constructor(x, y, radius, color) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
  }

  draw = function() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
    ctx.strokeStyle = this.color;
    ctx.stroke();
  };

  update = function() {
    cvs.addEventListener("click", function(e) {
      var mousePosition = this.getMousePosition(e);
      this.checkForCollision(mousePosition);
    }.bind(this));
    this.draw();
  };

  getMousePosition = function(e) {
    var cRect = cvs.getBoundingClientRect();
    var canvasX = Math.round(e.clientX - cRect.left);
    var canvasY = Math.round(e.clientY - cRect.top);
    return {
      x: canvasX,
      y: canvasY
    }
  }

  checkForCollision = function(mPos) {
    if (mPos.x < this.x + this.radius && mPos.x > this.x - this.radius && mPos.y < this.y + this.radius && mPos.y > this.y - this.radius) {
      this.color = "blue";
    }
  }
}

var circles = [];
for (x = 0; x < 8; x++) {
  for (y = 0; y < 8; y++) {
    circles.push(new Circle(x * 100 + 30, y * 100 + 30, 20, "red"));
  }
}

function playGame() {
  requestAnimationFrame(playGame);
  ctx.clearRect(0, 0, 2000, 2000);
  circles.forEach(circle => circle.update());
}

playGame();
<canvas id="canvas"></canvas>

目前,由于调用的位置 addEventListener,您正在为每个绘制周期添加一个事件侦听器。如果您改为将 addEventListener 移动到圆的构造函数,则每个 Circle 对象只会附加一次(在您的情况下为 64 次):

var cvs = document.getElementById("canvas");

var ctx = cvs.getContext("2d");

class Circle {
    constructor(x, y, radius, color) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;
        cvs.addEventListener("click", function (e) {
            var mousePosition = this.getMousePosition(e);
            this.checkForCollision(mousePosition);
            console.log('Click detected!');
        }.bind(this));
    }

    draw = function () {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
        ctx.strokeStyle = this.color;
        ctx.stroke();
    };

    update = function () {
        this.draw();
    };

    getMousePosition = function (e) {
        var cRect = cvs.getBoundingClientRect();
        var canvasX = Math.round(e.clientX - cRect.left);
        var canvasY = Math.round(e.clientY - cRect.top);
        return {
            x: canvasX,
            y: canvasY
        }
    }

    checkForCollision = function (mPos) {
        if (mPos.x < this.x + this.radius && mPos.x > this.x - this.radius && mPos.y < this.y + this.radius && mPos.y > this.y - this.radius) {
            this.color = "blue";
        }
    }
}

var circles = [];
for(x = 0; x < 8; x++){
    for(y = 0; y < 8; y++){
        circles.push(new Circle(x * 100 + 30, y * 100 + 30, 20, "red"));
    }
}

function playGame() {
    requestAnimationFrame(playGame);
    ctx.clearRect(0, 0, 2000, 2000);
    circles.forEach(circle => circle.update());
}

playGame();
<canvas id="canvas" width="1000" height="1000"> </canvas>

如果您只想在每次点击时触发一次点击事件,请将 getMousePosition 函数移出您的 Circle class 并循环遍历圆圈数组并检查点击是否在圆圈内他们的边界改为:

var cvs = document.getElementById("canvas");

var ctx = cvs.getContext("2d");

class Circle {
    constructor(x, y, radius, color) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;
    }

    draw = function () {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
        ctx.strokeStyle = this.color;
        ctx.stroke();
    };

    update = function () {
        this.draw();
    };

    checkForCollision = function(mPos) {
        if (mPos.x < this.x + this.radius && mPos.x > this.x - this.radius && mPos.y < this.y + this.radius && mPos.y > this.y - this.radius) {
            this.color = "blue";
            return true;
        }
        return false;
    }
}

var circles = [];
for(x = 0; x < 8; x++){
    for(y = 0; y < 8; y++){
        circles.push(new Circle(x * 100 + 30, y * 100 + 30, 20, "red"));
    }
}

function playGame() {
    requestAnimationFrame(playGame);
    ctx.clearRect(0, 0, 2000, 2000);
    circles.forEach(circle => circle.update());
}

function getMousePosition(e) {
    var cRect = cvs.getBoundingClientRect();
    var canvasX = Math.round(e.clientX - cRect.left);
    var canvasY = Math.round(e.clientY - cRect.top);
    return {
        x: canvasX,
        y: canvasY
    }
}

cvs.addEventListener("click", function (e) {
    console.log('Click detected!');
    var mousePosition = getMousePosition(e);
    circles.some(circle => {
        return circle.checkForCollision(mousePosition);
    });
});

playGame();
<canvas id="canvas" width="1000" height="1000"> </canvas>

您在每个绘制周期中都添加了一个新的点击事件,加起来很快。尝试将 addEventListener 移到 draw/update 循环之外。

还有event bubbling can cause the function to fire multiple times. This can however easily be fixed with event.stopPropagation().

EventListener 在 class.

之外

var cvs = document.getElementById("canvas");

var ctx = cvs.getContext("2d");

class Circle {
    constructor(x, y, radius, color) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;
    }

    draw = function () {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
        ctx.strokeStyle = this.color;
        ctx.stroke();
    };

    update = function () {
        this.draw();
    };

    getMousePosition = function (e) {
        var cRect = cvs.getBoundingClientRect();
        var canvasX = Math.round(e.clientX - cRect.left);
        var canvasY = Math.round(e.clientY - cRect.top);
        return {
            x: canvasX,
            y: canvasY
        }
    }

    checkForCollision = function (mPos) {
        if (mPos.x < this.x + this.radius && mPos.x > this.x - this.radius && mPos.y < this.y + this.radius && mPos.y > this.y - this.radius) {
            this.color = "blue";
        }
    }
}

var circles = [];
for(x = 0; x < 8; x++){
    for(y = 0; y < 8; y++){
        circles.push(new Circle(x * 100 + 30, y * 100 + 30, 20, "red"));
    }
}

cvs.addEventListener("click", function (e) {
  e.stopPropagation();
  for(const circle of circles) {
    var mousePosition = circle.getMousePosition(e);
    circle.checkForCollision(mousePosition);
  }
});

function playGame() {
    requestAnimationFrame(playGame);
    ctx.clearRect(0, 0, 2000, 2000);
    
    for(const circle of circles) {
       circle.update();
    }
}

playGame();
canvas {
  width: 500px;
  height: 500px;
}
<canvas id="canvas"></canvas>

构造函数中的 EventListener。

var cvs = document.getElementById("canvas");

var ctx = cvs.getContext("2d");

class Circle {
    constructor(x, y, radius, color) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;
        
        cvs.addEventListener("click", function (e) {
            e.stopPropagation()
            var mousePosition = this.getMousePosition(e);
            this.checkForCollision(mousePosition);
        }.bind(this));
    }

    draw = function () {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
        ctx.strokeStyle = this.color;
        ctx.stroke();
    };

    update = function () {
        this.draw();
    };

    getMousePosition = function (e) {
        var cRect = cvs.getBoundingClientRect();
        var canvasX = Math.round(e.clientX - cRect.left);
        var canvasY = Math.round(e.clientY - cRect.top);
        return {
            x: canvasX,
            y: canvasY
        }
    }

    checkForCollision = function (mPos) {
        if (mPos.x < this.x + this.radius && mPos.x > this.x - this.radius && mPos.y < this.y + this.radius && mPos.y > this.y - this.radius) {
            this.color = "blue";
        }
    }
}

var circles = [];
for(x = 0; x < 8; x++){
    for(y = 0; y < 8; y++){
        circles.push(new Circle(x * 100 + 30, y * 100 + 30, 20, "red"));
    }
}

function playGame() {
    requestAnimationFrame(playGame);
    ctx.clearRect(0, 0, 2000, 2000);
    
    for(const circle of circles) {
       circle.update();
    }
}

playGame();
canvas {
  width: 500px;
  height: 500px;
}
<canvas id="canvas"></canvas>