Как создать объект, который может изменять свой исходный вызов функции

#javascript #button #canvas

Вопрос:

Я пытаюсь создать объект button, который может запускать функцию класса (в данном случае show ()), которая изменит цвет кнопки. Проблема в том, что я могу сделать это, только вызывая show() повторно каждый раз, когда меняется цвет. В результате на экране появляется тонна кнопок, но я хотел бы нарисовать кнопку только один раз. Есть ли способ обойти это? (Примечание: У меня увеличена позиция кнопки x, чтобы показать создаваемые кнопки)

 let canvas = document.getElementById("JScanvas"),
  c = canvas.getContext("2d");

let mousePosition = {
  x: 0,
  y: 0
};
// ignore, a few functions I might need for this to run

function buildRect(fillColor, outlineColor, outlineSize, x, y, w, h) {
  if (fillColor amp;amp; outlineColor) {
    c.beginPath();
    c.rect(x, y, w, h);
    c.fillStyle = fillColor;
    c.fill();
    c.lineWidth = outlineSize;
    c.strokeStyle = outlineColor;
    c.stroke();
  } else if (fillColor amp;amp; !outlineColor) {
    c.beginPath();
    c.rect(x, y, w, h);
    c.fillStyle = fillColor;
    c.fill();
  } else if (!fillColor amp;amp; outlineColor) {
    c.beginPath();
    c.rect(x, y, w, h);
    c.lineWidth = outlineSize;
    c.strokeStyle = outlineColor;
    c.stroke();
  }
}

function write(str, x, y, color, txtSize, font) {
  let size = txtSize.toString();
  c.font = size   "px"   " "   font;
  c.fillStyle = color;
  c.fillText(str, x, y);
}
// end of useless functions

class button {
  // mouse is {canvs: canvas, mClicked: t/f, mPosition: mousePosition{x, y}}
  constructor(name, order, btnColor, x, y, w, h, txtColor, txtSize, m, f) {
    this.name = name;
    this.order = order;
    this.btnColor = btnColor;
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.txtColor = txtColor;
    this.txtSize = txtSize;
    this.m = m;
    this.f = f;

    //change color when mouse over button
    this.m.canvs.addEventListener("mousemove", (event) => {
      console.log("hello there");

      if (this.x < this.m.mPosition.x amp;amp; this.m.mPosition.x < this.x   this.w amp;amp; this.y < this.m.mPosition.y amp;amp; this.m.mPosition.y < this.y   this.h) {

        if (this.btnColor[0] != "grey") {
          this.btnColor[0] = "grey";
          this.x  = 50;
          this.show();
        } else {
          this.btnColor[0] = "red";
          this.x  = 50;
          this.show();
        }
      }
    });
  }

  show() {
    if (!this.btnColor[0] amp;amp; !this.btnColor[0]) {
      buildRect("transparent", false, 1, this.x, this.y, this.w, this.h);
    } else if (!this.btnColor[0]) {
      buildRect(false, this.btnColor[1], 1, this.x, this.y, this.w, this.h);
    } else if (!this.btnColor[1]) {
      buildRect(this.btnColor[0], false, 1, this.x, this.y, this.w, this.h);
    } else {
      buildRect(this.btnColor[0], this.btnColor[1], 1, this.x, this.y, this.w, this.h);
    }
    c.fillStyle = this.txtColor;
    let theString = String(this.txtSize)   "px Arial";
    c.font = theString;
    let width = Math.round(c.measureText(c.fillText(this.name, -1000, 0)).width);

    if (width > this.w) {
      let center = this.x   (this.w / 2);
      let newSize = this.w / width;
      c.font = String(this.txtSize * newSize);
      let newWidth = Math.round(newSize * width);
      c.textAlign = "center";
      c.textBaseline = "middle";
      c.fillText(this.name, this.x   (this.w / 2), this.y   (this.h / 2));

    } else {
      c.textAlign = "center";
      c.textBaseline = "middle";
      c.fillText(this.name, this.x   (this.w / 2), this.y   (this.h / 2));
    }
  }

  clickButton(mouseX, mouseY) {
    if (mouseX >= this.x amp;amp; mouseX <= this.x   this.w amp;amp; mouseY >= this.y amp;amp; mouseY <= this.y   this.h) {
      return true;
    } else {
      return false;
    }
  }

  runf() {
    this.f();
  }
}

//getting mouse position 

function getMousePos(canvas, evt) {
  const rect = canvas.getBoundingClientRect();
  return {
    x: evt.clientX - rect.left,
    y: evt.clientY - rect.top
  };
}

canvas.addEventListener("mousemove", (evt) => {
  let r = getMousePos(canvas, event);
  mousePosition.x = r.x, mousePosition.y = r.y;
});

let mouse = {
  canvs: canvas,
  mClicked: false,
  mPosition: mousePosition
};

//button object to call: ("red" is the color of the button I am trying to change to grey)
let cookie = new button("cookie", 1, ["red", false], 50, 100, 150, 50, "black", 30, mouse, 2);
cookie.show(); 
 <!DOCTYPE html>

<html>

<head>
  <title>Playground</title>

</head>

<body>

  <canvas width="1500" height="1500" id="JScanvas"></canvas>
</body>

</html> 

Комментарии:

1. Вы должны запомнить старое положение кнопки, чтобы вы могли удалить его, прежде чем рисовать новую кнопку.

2. Возможно, вам следует реализовать кнопку в виде спрайта, чтобы вы могли анимировать ее, а не просто рисовать новые прямоугольники.

3. @Бармар Интересно. Если я сохраню старую позицию, как я удалю старый прямоугольник, учитывая, что он был вызван в исходном show()? Я мог бы сделать спрайт, я полагаю, но разве нет другого способа?

4. Вы можете нарисовать его снова, используя цвет фона, который заставляет его исчезнуть.

5. @Barmar Рисуя его в качестве цвета фона, он действительно исчезает на экране, но я думаю, что код все еще работает. Он продолжает накапливать избыточные отходы.

Ответ №1:

Я думаю, что вы слишком усложнили свой код…

Вот простой пример, где мы меняем цвет, когда мышь находится над прямоугольником

 let canvas = document.getElementById("canvas")
let c = canvas.getContext("2d");

class button {
  constructor(color, x, y, w, h) {
    this.color = color;
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }

  draw(mouse) {
    c.beginPath();
    c.rect(this.x, this.y, this.w, this.h);
    if (mouse.x >= this.x amp;amp; mouse.x <= this.x   this.w amp;amp;
      mouse.y >= this.y amp;amp; mouse.y <= this.y   this.h) {
      c.fillStyle = "gray";
    } else {
      c.fillStyle = this.color;
    }
    c.fill();
  }
}

function getMousePos(evt) {
  if (!evt) return { x: 0, y: 0 }
  const rect = canvas.getBoundingClientRect();
  return { x: evt.clientX - rect.left, y: evt.clientY - rect.top };
}

let buttons = []
buttons.push(new button("red", 50, 100, 40, 20))
buttons.push(new button("blue", 100, 50, 40, 20))

canvas.addEventListener("mousemove", mainDraw);

function mainDraw(evt) {
  let mouse = getMousePos(evt)
  c.clearRect(0, 0, canvas.width, canvas.height);
  buttons.forEach((btn) => {
    btn.draw(mouse)
  })
}

mainDraw() 
 <canvas id="canvas" width="150" height="150"></canvas> 

Как вы можете видеть, есть только один addEventListener, и он находится за пределами кнопки класса, логика проста, мы очищаем весь холст и рисуем все на каждом событии… вот как это делают игры, я уверен, что это сработает и для того, что вы делаете.