MickDom MickDom - 7 months ago 30
HTML Question

How to make custom drawn buttons on html5 canvas work

I am making a html game and want to add onscreen buttons.

To do this I think I need to know what pixel the mouse is at when I click.

If anyone knows how to do that or has another way to make buttons LMK.

Answer

Here are the basics of finding a mouse position:

  1. Get a reference to the DOM element on which you want to listen for mouse events.

    var canvas=document.getElementById("canvas");
    
  2. The browser reports mouse coordinates relative to the top-left of the window rather than relative to your canvas element, so you need to account for the difference between the window & canvas positions. Use .getBoundingClientRect to fetch the offset of the canvas element within the window.

    // save the canvas offset in global variables
    var BB=canvas.getBoundingClientRect();
    var BBoffsetX=BB.left;
    var BBoffsetY=BB.top;
    
  3. Tell the browser you want to be told when the user does something with the mouse by subscribing to mouse events. You can do this using .onmousedown, .onmousemove, .onmouseup & .onmouseout and giving the browser functions to execute when these events occur.

    // listen for mousedown events, call handleMousedown() when they occur
    // listen for mousemove events, call handleMousemove() when they occur
    // listen for mouseup events, call handleMouseup() when they occur
    // listen for mouseout events, call handleMouseout() when they occur
    
    canvas.onmousedown=handleMousedown;
    canvas.onmousemove=handleMousemove;
    canvas.onmouseup=handleMouseup;
    canvas.onmouseout=handleMouseup;
    
  4. Respond to mouse events by coding mouse handler functions. The browser will automatically send an mouseevent argument when it executes the handler. That mouseevent contains clientX & clientY properties which are the X,Y coordinates relative to the top-left corner of the visible client area. To get the mouse position inside the canvas you must subtract BBoffsetX & BBoffsetY which were calculated in step#2.

    // this function is called every time the user presses the mouse down.
    // "e" is the mouseevent argument the browser automatically sends 
    function handleMousedown(e){
         // calculate the mouse position RELATIVE TO THE CANVAS
         var mouseX=e.clientX-BBoffsetX;
         var mouseY=e.clientY-BBoffsetY;
    
         // you have the mouse position so now do your button stuff
    }
    
  5. A refinement if the window will be scrolled...If the page content is larger than will fit in the browser display area, the browser will add scrollbars to let the user scroll to view all the page content. When the window scrolls, your offsets calculated in step#2 will become invalid so they must be recalculated. You can subscribe to the window.onscroll event and recalculate the offsets when scrolling occurs.

    // listen for events that invalidate the canvas offsets
    window.onscroll=function(e){ setBB(); }
    window.onresize=function(e){ setBB(); }
    
    // recalculate the offsets
    function setBB(){
        BB=canvas.getBoundingClientRect();
        BBoffsetX=BB.left;
        BBoffsetY=BB.top;
    }        
    

About your custom buttons

Here's a simple button system...

  • It displays (very!) simple custom drawn buttons,
  • It also shows how to get the mouse click position when mouse events occur,
  • It shows how to hit-test which custom drawn button the mouse is over,
  • It redraws the "hit" button to indicate it is pressed or released.

...You're welcome to start with it...

var $clicked=$('#clicked');

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
    var BB=canvas.getBoundingClientRect();
    offsetX=BB.left;
    offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }

var clickedButton;
var buttons=[];
buttons.push(makeButton(1,20,20,50,20,'One','skyblue','gray','black',
    function(){ $clicked.text('You clicked: '+this.id+' with label: '+this.label); },
    function(){ $clicked.text('You released: '+this.id+' with label: '+this.label); }
));
buttons.push(makeButton(2,20,50,50,20,'Two','lightgreen','gray','black',
    function(){ $clicked.text('You clicked: '+this.id+' with label: '+this.label); },
    function(){ $clicked.text('You released: '+this.id+' with label: '+this.label); }
));

//
drawAll();

//
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});


function makeButton(id,x,y,w,h,label,fill,stroke,labelcolor,clickFn,releaseFn){
    return({
        id:id,
        x:x, y:y, w:w, h:h,
        fill:fill, stroke:stroke, labelcolor:labelcolor,
        label:label,
        click:clickFn,
        release:releaseFn
    });
}

function drawAll(){
    for(var i=0;i<buttons.length;i++){
        drawButton(buttons[i],false);
    }
}

function drawButton(b,isDown){
    ctx.clearRect(b.x-1,b.y-1,b.w+2,b.h+2);
    ctx.fillStyle=b.fill;
    ctx.fillRect(b.x,b.y,b.w,b.h);
    ctx.strokeStyle=b.stroke;
    ctx.strokeRect(b.x,b.y,b.w,b.h);
    ctx.textAlign='center';
    ctx.textBaseline='middle';
    ctx.fillStyle=b.labelcolor;
    ctx.fillText(b.label,b.x+b.w/2,b.y+b.h/2);
    if(isDown){
        ctx.beginPath();
        ctx.moveTo(b.x,b.y+b.h);
        ctx.lineTo(b.x,b.y);
        ctx.lineTo(b.x+b.w,b.y);
        ctx.strokeStyle='black';
        ctx.stroke();
    }
}

function findButton(mx,my){
    for(var i=0;i<buttons.length;i++){
        var b=buttons[i];
        if(mx>b.x && mx<b.x+b.w && my>b.y && my<b.y+b.h){
            return(buttons[i]);
        }
    }
    return(null);
}

function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // check if a button was clicked under the mouse
  var b=findButton(mouseX,mouseY);
  if(b){
      clickedButton=b;
      drawButton(b,true);
      b.click();
  }
}

function handleMouseUpOut(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  
  // release any clicked button
  if(clickedButton){
      drawButton(clickedButton,false);
      clickedButton.release();
      clickedButton=null;
  }
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4 id=clicked>Click a button.</h4>
<canvas id="canvas" width=300 height=300></canvas>