简体   繁体   中英

How to draw parallel rectangles with rubber-band in canvas

I am trying to draw a group of shapes using canvas.

I have referenced below SO threads:

Draw a parallel line
How to draw parallel line using three.js?

but not able to figure out how to calculate points for the rectangles parallel in as we stretch the line.

Any reference for stretching shapes with canvas is appreciated.

 //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, ie #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, ie #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> 

I'm guessing this is what you wanted.

Instead of trying to manually draw your line rotated, instead, move the origin of the canvas to the start of the line,

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

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

then rotate the origin so it points toward the end of the line in the positive X direction

    // 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);

then compute the length of the line from the start to the end

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

and just draw an arrow in the positive x direction of that length

    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();

while we're at it your code for calculating the mouse position didn't work if the page is scrolled. This will get the mouse position relative to the pixels in the canvas.

  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, ie #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, ie #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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM