user3266510
user3266510

Reputation: 81

drag element on canvas

I draw an element onto a canvas as a string. I then want to be able to click the element (or tap on a mobile device) and drag it around. What I have now is:

<!DOCTYPE html>
<html>
    <head>
         <link rel="stylesheet" href="bootstrap.min.css" />
        <link type="text/css" rel="stylesheet" href="stylesheet.css"/>
        <script src="jquery-1.11.0.js"></script>
    </head>
    <body>
         <canvas id="canvas1" width="662" height="983">
             <script src="jquery-1.11.0.js"></script>
        </canvas>

        <script>
            var chosenChord=""; //this gets updated at various times by excluded code, but that code works, because the chord does always print correctly below
    `       $("button").click(function() {
                canvas = document.getElementById('canvas1');
                canvas1.addEventListener('click', on_canvas_click, false);

                function on_canvas_click(ev) {
                    x = ev.clientX - canvas1.offsetLeft-40;
                    y = ev.clientY - canvas1.offsetTop;

                   //add to canvas
                    var canvas = document.getElementById("canvas1");
                    var context = canvas.getContext("2d");
                    context.fillStyle = "blue";
                    context.font = "bold 16px Arial";
                    context.fillText([theString], [x], [y]);
                });
            });
        </script>   
    </body>
</html>

What do I need to add in order to enable this drag functionality?

Upvotes: 4

Views: 10879

Answers (2)

hendrikswan
hendrikswan

Reputation: 2341

I think the answer by @markE is really good. I would just like to add my 2c though.

Working with canvas directly can become daunting very quickly, as you have to do all sorts of state management on your own.

I think it's wise when you want to do something like this to rather utilize one of the many amazing frameworks out there that aims to make vector drawing on canvas easier.

To name but a few:

Upvotes: 0

markE
markE

Reputation: 105035

Here's an outline of how to drag an element on canvas

A Demo: http://jsfiddle.net/m1erickson/AGd6u/

Listen for mouse events: mousedown, mousemove, mouseup, mouseout

    // listen for mouse events
    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});

Define your pieces of text as objects in an array

    // some text objects
    var texts=[];

    // some test texts
    texts.push({text:"Hello",x:20,y:20});
    texts.push({text:"World",x:20,y:70});

In mousedown:

  • Test if the mouse is over the text
  • If yes, select that text for dragging

    // handle mousedown events
    // iterate through texts[] and see if the user
    // mousedown'ed on one of them
    // If yes, set the selectedText to the index of that text
    function handleMouseDown(e){
      e.preventDefault();
      startX=parseInt(e.clientX-offsetX);
      startY=parseInt(e.clientY-offsetY);
      // Put your mousedown stuff here
      for(var i=0;i<texts.length;i++){
          if(textHittest(startX,startY,i)){
              selectedText=i;
          }
      }
    }
    

In mousemove:

  • change the position of the selected text by the distance the user has dragged
  • redraw the canvas

    // handle mousemove events
    // calc how far the mouse has been dragged since
    // the last mousemove event and move the selected text
    // by that distance
    function handleMouseMove(e){
      if(selectedText<0){return;}
      e.preventDefault();
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
    
      // Put your mousemove stuff here
      var dx=mouseX-startX;
      var dy=mouseY-startY;
      startX=mouseX;
      startY=mouseY;
    
      var text=texts[selectedText];
      text.x+=dx;
      text.y+=dy;
      draw();
    }
    

In mouseup:

  • the drag is over so deselect the text

    // done dragging
    function handleMouseUp(e){
      e.preventDefault();
      selectedText=-1;
    }
    

Example code:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    // variables used to get mouse position on the canvas
    var $canvas=$("#canvas");
    var canvasOffset=$canvas.offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;
    var scrollX=$canvas.scrollLeft();
    var scrollY=$canvas.scrollTop();

    // variables to save last mouse position
    // used to see how far the user dragged the mouse
    // and then move the text by that distance
    var startX;
    var startY;

    // some text objects
    var texts=[];

    // some test texts
    texts.push({text:"Hello",x:20,y:20});
    texts.push({text:"World",x:20,y:70});

    // calculate width of each text for hit-testing purposes
    ctx.font="16px verdana";
    for(var i=0;i<texts.length;i++){
        var text=texts[i];
        text.width=ctx.measureText(text.text).width;
        text.height=16;
    }

    // this var will hold the index of the selected text
    var selectedText=-1;

    // START: draw all texts to the canvas
    draw();

    // clear the canvas draw all texts
    function draw(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        for(var i=0;i<texts.length;i++){
            var text=texts[i];
            ctx.fillText(text.text,text.x,text.y);
        }
    }

    // test if x,y is inside the bounding box of texts[textIndex]
    function textHittest(x,y,textIndex){
        var text=texts[textIndex];
        return(x>=text.x && 
            x<=text.x+text.width &&
            y>=text.y-text.height && 
            y<=text.y);
    }

    // handle mousedown events
    // iterate through texts[] and see if the user
    // mousedown'ed on one of them
    // If yes, set the selectedText to the index of that text
    function handleMouseDown(e){
      e.preventDefault();
      startX=parseInt(e.clientX-offsetX);
      startY=parseInt(e.clientY-offsetY);

      // Put your mousedown stuff here
      for(var i=0;i<texts.length;i++){
          if(textHittest(startX,startY,i)){
              selectedText=i;
          }
      }
    }

    // done dragging
    function handleMouseUp(e){
      e.preventDefault();
      selectedText=-1;
    }

    // also done dragging
    function handleMouseOut(e){
      e.preventDefault();
      selectedText=-1;
    }

    // handle mousemove events
    // calc how far the mouse has been dragged since
    // the last mousemove event and move the selected text
    // by that distance
    function handleMouseMove(e){
      if(selectedText<0){return;}
      e.preventDefault();
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // Put your mousemove stuff here
      var dx=mouseX-startX;
      var dy=mouseY-startY;
      startX=mouseX;
      startY=mouseY;

      var text=texts[selectedText];
      text.x+=dx;
      text.y+=dy;
      draw();
    }

    // listen for mouse events
    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});

}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

Upvotes: 7

Related Questions