首页 > 解决方案 > 如何在画布中用橡皮筋绘制平行矩形

问题描述

我正在尝试使用画布绘制一组形状。

我在下面引用了 SO 线程:

绘制平行线
如何使用three.js 绘制平行线?

但无法弄清楚如何在我们拉伸线时计算平行矩形的点。

任何使用画布拉伸形状的参考都值得赞赏。

//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;

  // grid parameters
  var gridSpacing = 20; // pixels
  var gridWidth = 1;
  //var gridColor = "#f1f1f1";
  var gridColor = "lightgray";
  /** */
    var originX = 0;

    /** */
    var originY = 0;
 drawGrid();
//Mousedown
$(canvas).on('mousedown', function(e) {
    last_mousex = parseInt(e.clientX-canvasx);
	last_mousey = parseInt(e.clientY-canvasy);
    mousedown = true;
});

//Mouseup
$(canvas).on('mouseup', function(e) {
    mousedown = false;
});

//Mousemove
$(canvas).on('mousemove', function(e) {
    mousex = parseInt(e.clientX-canvasx);
	mousey = parseInt(e.clientY-canvasy);
    if(mousedown) {    
        ctx.clearRect(0,0,canvas.width,canvas.height); //clear canvas
        drawGrid();
        ctx.setLineDash([5, 15]);
        ctx.beginPath();
        ctx.moveTo(last_mousex,last_mousey);
        ctx.lineTo(mousex,mousey);
        //ctx.lineTo(mousex,mousey);
        ctx.strokeStyle = 'blue';
        ctx.lineDashOffset = 2;
        ctx.lineWidth = 5;
        ctx.lineJoin = ctx.lineCap = 'round';       
        ctx.stroke();
        
        startx = last_mousex;
        starty = last_mousey;
        drawPolygon([last_mousex, mousex, mousex, last_mousex, last_mousex], 	
                    [last_mousey-10, mousey-10, mousey-60, last_mousey-60],true, 'gray', false, 'black', 2);
        
        drawPolygon([last_mousex, mousex, mousex, last_mousex, last_mousex], 	
                    [last_mousey+10, mousey+10, mousey+60, last_mousey+60],true, 'gray', false, 'black', 2);
        
        
        
    }
    //Output
    $('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});

/** */
    function drawLine(startX, startY, endX, endY, width, color) {
      // width is an integer
      // color is a hex string, i.e. #ff0000
      ctx.beginPath();
      ctx.moveTo(startX, startY);
      ctx.lineTo(endX, endY);
      ctx.lineWidth = width;
      ctx.strokeStyle = color;
      ctx.stroke();
    }


function drawPolygon(xArr, yArr, fill, fillColor, stroke, strokeColor, strokeWidth) {
      // fillColor is a hex string, i.e. #ff0000
      fill = fill || false;
      stroke = stroke || false;
      ctx.beginPath();
      ctx.moveTo(xArr[0], yArr[0]);
      for (var i = 1; i < xArr.length; i++) {
        ctx.lineTo(xArr[i], yArr[i]);
      }
      ctx.closePath();
      if (fill) {
        ctx.fillStyle = fillColor;
        ctx.fill();
      }
      if (stroke) {
        ctx.lineWidth = strokeWidth;
        ctx.strokeStyle = strokeColor;
        ctx.stroke();
      }
      //console.log(xArr);
      //console.log(yArr);
      
    }
    
    /** returns n where -gridSize/2 < n <= gridSize/2  */
    function calculateGridOffset(n) {
      if (n >= 0) {
        return (n + gridSpacing / 2.0) % gridSpacing - gridSpacing / 2.0;
      } else {
        return (n - gridSpacing / 2.0) % gridSpacing + gridSpacing / 2.0;
      }
    }

    /** */
    function drawGrid() {
      var offsetX = calculateGridOffset(-originX);
      var offsetY = calculateGridOffset(-originY);
      var width = canvas.width;
      var height = canvas.height;
      for (var x = 0; x <= (width / gridSpacing); x++) {
        drawLine(gridSpacing * x + offsetX, 0, gridSpacing * x + offsetX, height, gridWidth, gridColor);
      }
      for (var y = 0; y <= (height / gridSpacing); y++) {
        drawLine(0, gridSpacing * y + offsetY, width, gridSpacing * y + offsetY, gridWidth, gridColor);
      }
    }
canvas {
    cursor: crosshair;
    border: 1px solid #000000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" width="800" height="500"></canvas>
<div id="output"></div>

标签: javascripthtml5-canvas

解决方案


我猜这就是你想要的。

与其尝试手动绘制旋转线,不如将画布的原点移动到线的开头,

    // save the canvas state
    ctx.save();

    // move origin to start of line
    ctx.translate(last_mousex, last_mousey);

然后旋转原点,使其指向正 X 方向的直线末端

    // compute direction of line from start to end
    const dx = mousex - last_mousex;
    const dy = mousey - last_mousey;
    const angle = Math.atan2(dy, dx);

    // rotate to point to end of line
    ctx.rotate(angle);

然后计算从开始到结束的线的长度

    // compute length of line
    const length = Math.sqrt(dx * dx + dy * dy);

并在该长度的正 x 方向上画一个箭头

    ctx.setLineDash([5, 15]);
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(length, 0);
    ctx.strokeStyle = 'blue';
    ctx.lineDashOffset = 2;
    ctx.lineWidth = 5;
    ctx.lineJoin = ctx.lineCap = 'round';       
    ctx.stroke();
    drawPolygon([0, length, length, 0, 0],  
                [-10, -10, -60, -60],true, 'gray', false, 'black', 2);

    drawPolygon([0, length, length, 0, 0],  
                [+10, +10, +60, +60],true, 'gray', false, 'black', 2);

    // restore the canvas state
    ctx.restore();

当我们在这里时,如果页面滚动,您用于计算鼠标位置的代码不起作用。这将获得鼠标相对于画布中像素的位置。

  const rect = canvas.getBoundingClientRect();
  mousex = (e.clientX - rect.left) * canvas.width  / canvas.clientWidth;
  mousey = (e.clientY - rect.top ) * canvas.height / canvas.clientHeight;

//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;

  // grid parameters
  var gridSpacing = 20; // pixels
  var gridWidth = 1;
  //var gridColor = "#f1f1f1";
  var gridColor = "lightgray";
  /** */
    var originX = 0;

    /** */
    var originY = 0;
 drawGrid();
//Mousedown
$(canvas).on('mousedown', function(e) {
  const rect = canvas.getBoundingClientRect();
  last_mousex = (e.clientX - rect.left) * canvas.width  / canvas.clientWidth;
	last_mousey = (e.clientY - rect.top ) * canvas.height / canvas.clientHeight;
    mousedown = true;
});

//Mouseup
$(canvas).on('mouseup', function(e) {
    mousedown = false;
});

//Mousemove
$(canvas).on('mousemove', function(e) {
  const rect = canvas.getBoundingClientRect();
  mousex = (e.clientX - rect.left) * canvas.width  / canvas.clientWidth;
	mousey = (e.clientY - rect.top ) * canvas.height / canvas.clientHeight;
    if(mousedown) {    
        ctx.clearRect(0,0,canvas.width,canvas.height); //clear canvas
        drawGrid();
        // save the canvas state
        ctx.save();

        // move origin to start of line
        ctx.translate(last_mousex, last_mousey);

        // compute direction of line from start to end
        const dx = mousex - last_mousex;
        const dy = mousey - last_mousey;
        const angle = Math.atan2(dy, dx);
        // rotate to point to end of line
        ctx.rotate(angle);

        // compute length of line
        const length = Math.sqrt(dx * dx + dy * dy);

        ctx.setLineDash([5, 15]);
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(length, 0);
        ctx.strokeStyle = 'blue';
        ctx.lineDashOffset = 2;
        ctx.lineWidth = 5;
        ctx.lineJoin = ctx.lineCap = 'round';       
        ctx.stroke();
        drawPolygon([0, length, length, 0, 0], 	
                    [-10, -10, -60, -60],true, 'gray', false, 'black', 2);
        
        drawPolygon([0, length, length, 0, 0], 	
                    [+10, +10, +60, +60],true, 'gray', false, 'black', 2);

        // restore the canvas state
        ctx.restore();        
    }
    //Output
    $('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});

/** */
    function drawLine(startX, startY, endX, endY, width, color) {
      // width is an integer
      // color is a hex string, i.e. #ff0000
      ctx.beginPath();
      ctx.moveTo(startX, startY);
      ctx.lineTo(endX, endY);
      ctx.lineWidth = width;
      ctx.strokeStyle = color;
      ctx.stroke();
    }


function drawPolygon(xArr, yArr, fill, fillColor, stroke, strokeColor, strokeWidth) {
      // fillColor is a hex string, i.e. #ff0000
      fill = fill || false;
      stroke = stroke || false;
      ctx.beginPath();
      ctx.moveTo(xArr[0], yArr[0]);
      for (var i = 1; i < xArr.length; i++) {
        ctx.lineTo(xArr[i], yArr[i]);
      }
      ctx.closePath();
      if (fill) {
        ctx.fillStyle = fillColor;
        ctx.fill();
      }
      if (stroke) {
        ctx.lineWidth = strokeWidth;
        ctx.strokeStyle = strokeColor;
        ctx.stroke();
      }
      //console.log(xArr);
      //console.log(yArr);
      
    }
    
    /** returns n where -gridSize/2 < n <= gridSize/2  */
    function calculateGridOffset(n) {
      if (n >= 0) {
        return (n + gridSpacing / 2.0) % gridSpacing - gridSpacing / 2.0;
      } else {
        return (n - gridSpacing / 2.0) % gridSpacing + gridSpacing / 2.0;
      }
    }

    /** */
    function drawGrid() {
      var offsetX = calculateGridOffset(-originX);
      var offsetY = calculateGridOffset(-originY);
      var width = canvas.width;
      var height = canvas.height;
      for (var x = 0; x <= (width / gridSpacing); x++) {
        drawLine(gridSpacing * x + offsetX, 0, gridSpacing * x + offsetX, height, gridWidth, gridColor);
      }
      for (var y = 0; y <= (height / gridSpacing); y++) {
        drawLine(0, gridSpacing * y + offsetY, width, gridSpacing * y + offsetY, gridWidth, gridColor);
      }
    }
canvas {
    cursor: crosshair;
    border: 1px solid #000000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" width="800" height="500"></canvas>
<div id="output"></div>


推荐阅读