user2539506
user2539506

Reputation:

Drawing a rectangle using click, mouse move, and click

I'm trying to draw a rectangle by a user click, mouse move, and click. There are two problems with my code.

Firstly, after one rectangle is drawn it is automatically assumed that another one will be drawn. Secondly, the starting point on the second rectangle is the last click that created the first rectangle.

http://jsbin.com/uqonuw/3/edit

Upvotes: 30

Views: 89783

Answers (5)

Sufiyan Ansari
Sufiyan Ansari

Reputation: 1946

Below is the solution I created in React. There might be some corner cases but it is working as per my knowledge.

Solution approach.

  1. You must have start (x,y position) and the end (x,y) position
  2. Once the user clicks on the cell capture cell number and column number start (x,y), this will happen on the mouseDown event
  3. Then the user starts moving the mouse, in that case capture the cell number and the row number end (x,y) respectively.
  4. Now the div draw logic comes where the condition would highlight the cell if the below condition is true.
  5. i = cellNumber
  6. i >= Math.min(start,end) && i <= Math.max(start,end) && i%4 <= Math.max(startY, endY)

enter image description here

https://codesandbox.io/s/still-field-0q760y?file=/src/App.js

Upvotes: 0

garbageoverflow
garbageoverflow

Reputation: 21

i was also working on a project, so here's my code enjoy.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Selection</title>
    <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
    <style>
        body {
            margin: 0px;
            background-color: #f1f1f1;
        }
        canvas {
            border: none;
        }
    </style>
</head>
<body>
    <canvas id="canvas" width="800" height="500"></canvas>
    <div id="output"></div>
    <script>
        //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;

//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;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
});

//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
        ctx.beginPath();
        var width = mousex-last_mousex;
        var height = mousey-last_mousey;
        ctx.rect(last_mousex,last_mousey,width,height);
        //ctx.fillStyle = "#8ED6FF";
        ctx.fillStyle = 'rgba(164, 221, 249, 0.3)'
        ctx.fill();
        ctx.strokeStyle = '#1B9AFF';
        ctx.lineWidth = 1;
        ctx.fillRect(last_mousex, last_mousey, width, height)
        ctx.stroke();
    }
    //Output
    $('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});
    </script>
</body>
</html>

Upvotes: 2

Barazu
Barazu

Reputation: 499

For those who encountered the scrolling problem, I've found a fix.
You need to get the offset (using window.pageYOffset) and reduce it from the mouse position in any of the recommended snippets given. You should take it off from the height as well.

Upvotes: 3

Spencer Lockhart
Spencer Lockhart

Reputation: 1174

You were close. So, the question isn't really about the "canvas" element in HTML5, but a canvas that is really a div.

http://jsfiddle.net/d9BPz/546/

In order for me to see what your code was trying to accomplish, I had to tidy it up. What needed to happen was tracking of the square element.

We are doing one of two things everytime we click on the canvas. We are either creating a rectangle element, or finishing a rectangle element. So, when we're finished it makes sense to set 'element' (previously named 'd') to null. When creating an element, we have to assign the new DOM element to 'element'.

Everytime the mouse moves, we want to get the mouse position. If the element is in the process of creation (or "not null"), then we need to resize the element.

Then we wrap it all up in a function, and that's all there is to it:

function initDraw(canvas) {
    var mouse = {
        x: 0,
        y: 0,
        startX: 0,
        startY: 0
    };
    function setMousePosition(e) {
        var ev = e || window.event; //Moz || IE
        if (ev.pageX) { //Moz
            mouse.x = ev.pageX + window.pageXOffset;
            mouse.y = ev.pageY + window.pageYOffset;
        } else if (ev.clientX) { //IE
            mouse.x = ev.clientX + document.body.scrollLeft;
            mouse.y = ev.clientY + document.body.scrollTop;
        }
    };

    var element = null;    
    canvas.onmousemove = function (e) {
        setMousePosition(e);
        if (element !== null) {
            element.style.width = Math.abs(mouse.x - mouse.startX) + 'px';
            element.style.height = Math.abs(mouse.y - mouse.startY) + 'px';
            element.style.left = (mouse.x - mouse.startX < 0) ? mouse.x + 'px' : mouse.startX + 'px';
            element.style.top = (mouse.y - mouse.startY < 0) ? mouse.y + 'px' : mouse.startY + 'px';
        }
    }

    canvas.onclick = function (e) {
        if (element !== null) {
            element = null;
            canvas.style.cursor = "default";
            console.log("finsihed.");
        } else {
            console.log("begun.");
            mouse.startX = mouse.x;
            mouse.startY = mouse.y;
            element = document.createElement('div');
            element.className = 'rectangle'
            element.style.left = mouse.x + 'px';
            element.style.top = mouse.y + 'px';
            canvas.appendChild(element)
            canvas.style.cursor = "crosshair";
        }
    }
}

Usage: Pass the block-level element that you would like to make a rectangle canvas. Example:

<!doctype html>
<html>
<head>
    <style>
    #canvas {
        width:2000px;
        height:2000px;
        border: 10px solid transparent;
    }
    .rectangle {
        border: 1px solid #FF0000;
        position: absolute;
    }
    </style>
</head>
<body>
    <div id="canvas"></div>
    <script src="js/initDraw.js"></script>
    <script>
        initDraw(document.getElementById('canvas'));
    </script>
</body>
</html>

Upvotes: 46

markE
markE

Reputation: 105015

Here's how to click-move-click to create a rectangle

Create these variables:

var isDrawing=false;
var startX;
var startY;

In your mousedown event handler:

  • If this is the starting click, set the isDrawing flag and set the startX/Y.
  • If this is the ending click, clear the isDrawing flage and draw the rectangle.

You might also want to change the mouse cursor so the user knows they are drawing.

if(isDrawing){
    isDrawing=false;
    ctx.beginPath();
    ctx.rect(startX,startY,mouseX-startX,mouseY-startY);
    ctx.fill();
    canvas.style.cursor="default";
}else{
    isDrawing=true;
    startX=mouseX;
    startY=mouseY;
    canvas.style.cursor="crosshair";
}

Here is a Fiddle: http://jsfiddle.net/m1erickson/7uNfW/

Instead of click-move-click, how about using drag to create a rectangle?

Create these variables:

var mouseIsDown=false;
var startX;
var startY;

In your mousedown event handler, set the mouseIsDown flag and set the startX/Y.

Optionally, change the cursor so the user knows their dragging a rectangle.

      mouseIsDown=true;
      startX=mouseX;
      startY=mouseY;
      canvas.style.cursor="crosshair";

In your mouseup event handler, clear the mouseIsDown flag and draw the rect

If you changed the cursor, change it back.

      mouseIsDown=false;
      ctx.beginPath();
      ctx.rect(startX,startY,mouseX-startX,mouseY-startY);
      ctx.fill();
      canvas.style.cursor="default";

Upvotes: 12

Related Questions