A continuación se muestra mi código cuando agrego eventlistener en mi objeto:

this.canvas.addEventListener('mousemove', function (e) {
    var canv = document.getElementById("some");
    myGameArea.x = (e.clientX - canv.getBoundingClientRect().left);//Look at canv
    myGameArea.y = (e.clientY - canv.getBoundingClientRect().top);//Look at canv
});

Funciona, pero solo cuando uso getElementById para encontrar mi elemento de lienzo y luego uso canv para mis necesidades. Pero si hago esto:

this.canvas.addEventListener('mousemove', function (e) {
    var canv = document.getElementById("some");
    myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
    myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
});

Entonces myGameArea no cambia en absoluto y mi depurador dice esto: "Error de tipo no capturado: no se puede leer la propiedad ' getBoundingClientRect ' de undefined en HTMLCanvasElement ". He declarado el lienzo en mi objeto antes de esta parte del código y usé this.canvas para muchas otras funciones. Entonces, ¿dónde está mi problema?

0
Zahar Zagrava 29 oct. 2017 a las 19:34

3 respuestas

La mejor respuesta

A diferencia de las variables normales, el valor de la palabra clave this no depende de dónde se define la función desde la que está accediendo, sino de cómo se llama. En su caso, this se sobrescribe en el controlador. Una solución rápida sería la siguiente:

var that = this;
this.canvas.addEventListener('mousemove', function (e) {
    myGameArea.x = (e.clientX - that.canvas.getBoundingClientRect().left);//Look at this.canvas
    myGameArea.y = (e.clientY - that.canvas.getBoundingClientRect().top);//Look at this.canvas
});

Aquí declaramos explícitamente el objeto al que queremos acceder y, por lo tanto, no necesitamos usar this en absoluto. Sin embargo, esto no se considera la mejor práctica. La forma recomendada de tratar con el ámbito anidado es bind la función al contexto que deseas:

this.canvas.addEventListener('mousemove', function (e) {
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
}.bind(this));

O, si está utilizando ES6, use la notación de flecha gruesa (() => {}), que no crea su propio contexto, para el controlador:

this.canvas.addEventListener('mousemove', (e) => {
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
});
1
stybl 29 oct. 2017 a las 16:47

En la función de devolución de llamada de addEventListener, this ya no es el mismo valor que estaba fuera de la devolución de llamada. Si desea enlazar al mismo valor this, puede usar .bind(this) o usar la función de flecha:

this.canvas.addEventListener('mousemove', function (e) {
  var canv = document.getElementById("some");
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
}.bind(this));

this.canvas.addEventListener('mousemove', (e) => {
  var canv = document.getElementById("some");
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
});
0
hackerrdave 29 oct. 2017 a las 16:37

Dentro del cierre de devolución de llamada, el contexto de this es el elemento del lienzo en sí. Por lo tanto, solo usarías this para hacer referencia al lienzo.

this.canvas.addEventListener('mousemove', function (e) {
    myGameArea.x = (e.clientX - this.getBoundingClientRect().left);
    myGameArea.y = (e.clientY - this.getBoundingClientRect().top);
});
2
Joshua Jones 29 oct. 2017 a las 16:46