我怎样才能在 HTML Canvas 中制作一条绑定到鼠标输入的直线

How can i make a single straight line in HTML Canvas which is bound to the input of the mouse

我有一个问题,我似乎无法找出正确的方法。对于某些上下文。我正在尝试制作一种点点滴滴的游戏。人们可以点击一个圈子,然后 link 圈子转到下一个圈子。连接点工作正常,但我想添加一个功能,当点击第一个点时,用户从点击的点到鼠标输入得到一条完美的直线。通过这种方式,用户可以获得关于他们点击了哪个点的一些反馈,以及他们如何 link 彼此。

这是一个 codePen 显示我想要实现的目标。唯一不同的是这支“笔”中的点是动画的,我希望它们静止不动。

到目前为止,我尝试了很多东西,并找到了多篇关于这个主题的 Whosebug 文章。到目前为止,我已经把这些点连接起来了。用户还可以在选择一个点时绘制一条线。然而,最大的问题是用户的每一个动作都会导致在 canvas 上画一条线。这行仅用作反馈选择了哪个点以及用户如何将光标绘制到下一个点,立即用数千个实例填满屏幕。我只想一次向他们显示一行,将他们的鼠标光标作为端点。就像我之前提到的codePen

到目前为止,这是我的 JS 代码。

    var dotColor = "#FF0000";
    var strokeColor = "#FF0000";
    var mouse = {
      x: undefined,
      y: undefined
    };

    var obj;


    var data = {
      canvas: null,
      ctx: null,
      clickedDot: null,
      dots: [{
        x: 180,
        y: 50
      }, {
        x: 20,
        y: 50
      }]
    };

    window.addEventListener('mousemove', function(e) {
      mouse.x = e.x;
      mouse.y = e.y;
      renderActiveLink();
    });

    function circleCollision(c1, c2) {
      var a = c1.r + c2.r,
        x = c1.x - c2.x,
        y = c1.y - c2.y;

      if (a > Math.sqrt((x * x) + (y * y))) return true;
      else return false;
    }

    function prepCanvas() {
      var res = window.devicePixelRatio || 1,
        scale = 1 / res;
      data.canvas = document.getElementById('dots');
      data.ctx = data.canvas.getContext('2d');

      data.canvas.width = 500;
      data.canvas.height = 300;

      data.ctx.scale(res, res);

      data.canvas.addEventListener('mousedown', function(e) {
        checkForDot(e);
      });
      data.canvas.addEventListener('mouseup', function(e) {
        checkForDot(e);
      });
    }

    function drawDots() {
      var i = 0;
      for (; i < data.dots.length; i++) {
        var d = data.dots[i];
        data.ctx.beginPath();
        data.ctx.arc(d.x, d.y, 5, 0, 2 * Math.PI);
        data.ctx.fillStyle = dotColor;
        data.ctx.fill();
        data.ctx.closePath();
      }
    }

    function drawLine(toDot) {
      data.ctx.beginPath();
      data.ctx.moveTo(data.clickedDot.x, data.clickedDot.y);
      data.ctx.lineTo(toDot.x, toDot.y);
      data.ctx.lineWidth = 5;
      data.ctx.strokeStyle = strokeColor;
      data.ctx.stroke();
      data.ctx.closePath();
    }

    function checkForDot(e) {
      var i = 0,
        col = null;
      for (; i < data.dots.length; i++) {
        var d = data.dots[i],
          c1 = {
            x: d.x,
            y: d.y,
            r: 50
          },
          c2 = {
            x: e.pageX,
            y: e.pageY,
            r: 50
          };
        if (circleCollision(c1, c2)) {
          col = d;
        }
      }
      if (col !== null) {
        if (data.clickedDot !== null) drawLine(col);
        data.clickedDot = col;
        obj = col;
      } else data.clickedDot = null;
    }

    function renderActiveLink() {
      data.ctx.beginPath();
      data.ctx.lineWidth = 5;
      data.ctx.shadowBlur = 0;
      data.ctx.moveTo(obj.x, obj.y);
      data.ctx.lineTo(mouse.x, mouse.y);
      data.ctx.strokeStyle = '#000000';
      data.ctx.stroke();
    }



    prepCanvas();
    drawDots();
*{
  margin:0;
  padding: 0;
}

#dots {
  border: 1px solid black;
}
<canvas id="dots"></canvas>

也可以在这个JSFiddle中看到。

希望这里有人能帮助我。如果需要更多信息,我很乐意回答您的问题:D.

此代码只是为了帮助您使用canvas

我在数据中添加了开始状态。

var data = {
  start: false, // initial value is false
  canvas: null,
  ctx: null,
  clickedDot: null,
  dots: [{
    x: 180,
    y: 50
  }, {
    x: 20,
    y: 50
  }]
};

并且我在 mousedownmouseup 听众中更改了开始状态。

      data.canvas.addEventListener('mousedown', function(e) {
        data.start = true
        checkForDot(e);
      });
      data.canvas.addEventListener('mouseup', function(e) {
        data.start = false
        checkForDot(e);
      });

所以renderActiveLink方法,只在起始位置为true时有效。 并在绘制每条线之前清除所有内容。

function renderActiveLink() {
      if(!data.start) return;
      data.ctx.clearRect(0, 0, data.canvas.width, data.canvas.height);
      drawDots();
      ...

最终代码:

var dotColor = "#FF0000";
    var strokeColor = "#FF0000";
    var mouse = {
      x: undefined,
      y: undefined
    };

    var obj;


    var data = {
      start: false,
      canvas: null,
      ctx: null,
      clickedDot: null,
      dots: [{
        x: 180,
        y: 50
      }, {
        x: 20,
        y: 50
      }]
    };

    window.addEventListener('mousemove', function(e) {
      mouse.x = e.x;
      mouse.y = e.y;
      renderActiveLink();
    });

    function circleCollision(c1, c2) {
      var a = c1.r + c2.r,
        x = c1.x - c2.x,
        y = c1.y - c2.y;

      if (a > Math.sqrt((x * x) + (y * y))) return true;
      else return false;
    }

    function prepCanvas() {
      var res = window.devicePixelRatio || 1,
        scale = 1 / res;
      data.canvas = document.getElementById('dots');
      data.ctx = data.canvas.getContext('2d');

      data.canvas.width = 500;
      data.canvas.height = 300;

      data.ctx.scale(res, res);
      
      data.canvas.addEventListener('mousedown', function(e) {
        data.start = true
        checkForDot(e);
      });
      data.canvas.addEventListener('mouseup', function(e) {
        data.start = false
        checkForDot(e);
      });
    }

    function drawDots() {
      var i = 0;
      for (; i < data.dots.length; i++) {
        var d = data.dots[i];
        data.ctx.beginPath();
        data.ctx.arc(d.x, d.y, 5, 0, 2 * Math.PI);
        data.ctx.fillStyle = dotColor;
        data.ctx.fill();
        data.ctx.closePath();
      }
    }

    function drawLine(toDot) {
      data.ctx.beginPath();
      data.ctx.moveTo(data.clickedDot.x, data.clickedDot.y);
      data.ctx.lineTo(toDot.x, toDot.y);
      data.ctx.lineWidth = 5;
      data.ctx.strokeStyle = strokeColor;
      data.ctx.stroke();
      data.ctx.closePath();
    }

    function checkForDot(e) {
      var i = 0,
        col = null;
      for (; i < data.dots.length; i++) {
        var d = data.dots[i],
          c1 = {
            x: d.x,
            y: d.y,
            r: 50
          },
          c2 = {
            x: e.pageX,
            y: e.pageY,
            r: 50
          };
        if (circleCollision(c1, c2)) {
          col = d;
        }
      }
      if (col !== null) {
        if (data.clickedDot !== null) drawLine(col);
        data.clickedDot = col;
        obj = col;
      } else data.clickedDot = null;
    }

    function renderActiveLink() {
      if(!data.start) return;
      data.ctx.clearRect(0, 0, data.canvas.width, data.canvas.height);
      drawDots();
      data.ctx.beginPath();
      data.ctx.lineWidth = 5;
      data.ctx.shadowBlur = 0;
      data.ctx.moveTo(obj.x, obj.y);
      data.ctx.lineTo(mouse.x, mouse.y);
      data.ctx.strokeStyle = '#000000';
      data.ctx.stroke();
    }



    prepCanvas();
    drawDots();
*{
  margin:0;
  padding: 0;
}

#dots {
  border: 1px solid black;
}
<canvas id="dots"></canvas>