Course notes 25
syllabus | schedule | exercises | assignments | class notes | resources | students | ARTC courses

context.isPointInPath()

The canvas specification provides a hit detection method for checking if a given point falls within a path being drawn. The method is only applicable to paths, and can only be called during the drawing phase. When called, it will check if the passed point falls within the area defined since the last beginPath() call. By passing the current mouse coordinates, we can actively track what objects a user is positioned to interact with.

context.isPointInPath(x,y);

The function will return a value of true or false. There are several useful ways to apply this information. As the below example demonstrates, it can be implemented immediately within the drawing process to dynamically determine parameters and dictate decisions. In the example it is used to change the fill color of the circle that the mouse is currently hovering over. This example also demonstrates one of the drawbacks of using isPointInPath for interaction. If two paths are overlapping, the mouse is technically within both paths. To avoid this, you will need to implement some logic to check that only one path is currently active.

var canvas,ctx, mouseX = 999, mouseY = 999,circles = new Array;
var num = Math.floor(Math.random()*30-10)+10;  

function init(){
    canvas = document.getElementById('canvas01');
    ctx = canvas.getContext('2d');
    
    for(var i=0; i < num; i++){
        circles[i] = {
        x: Math.floor(Math.random()*canvas.width),
        y : Math.floor(Math.random()*canvas.height),
        r : Math.floor(Math.random()*60-10)+10
        }
    }
    drawCanvas();
    canvas.addEventListener('mousemove',updateCanvas,false);
    
}

function findOffset(obj) {
    var curX = curY = 0;
    if (obj.offsetParent) {
        do {
            curX += obj.offsetLeft;
            curY += obj.offsetTop;
        } while (obj = obj.offsetParent);
    return {x:curX,y:curY};
    }
}

function updateCanvas(e){
    var pos = findOffset(canvas);
    
    mouseX = e.pageX - pos.x;
    mouseY = e.pageY - pos.y;
    
    ctx.clearRect(0,0,canvas.width,canvas.height);
    drawCanvas();
}


function drawCanvas() {
    
    for(var i = 0; i < num; i++){
        ctx.beginPath();
        ctx.fillStyle = 'rgba(0,0,0,.5)';
        
        ctx.arc(circles[i].x,circles[i].y,circles[i].r,0,Math.PI*2,false);
        if(ctx.isPointInPath(mouseX,mouseY)){
            ctx.fillStyle = 'red';
        }
        ctx.fill();
    }
}

The next example uses the value returned from isPointInPath() to change the contents of the canvas at a later point in time. The value is stored as a parameter in a reference to the canvas path, and then, later, is used to determine if the path should be drawn. This is also an example of how multiple event listeners can be used in conjunction to facilitate more complex interactions.

var canvas,ctx,mouseX = 999, mouseY = 999,bubbles = new Array;
var num = Math.floor(Math.random()*30-10)+10;

function init(){
    canvas = document.getElementById('canvas02');
    ctx = canvas.getContext('2d');
    
    for(var i=0; i < num; i++){
        bubbles[i] = {
        x: Math.floor(Math.random()*canvas.width),
        y : Math.floor(Math.random()*canvas.height),
        r : Math.floor(Math.random()*60-10)+10,
        mouse: false,
        }
    }
    
    drawCanvas();
    canvas2.addEventListener('mousemove',updateCanvas,false);
    canvas2.addEventListener('click',popBubble,false);
    
    
}

function findOffset(obj) {
    var curX = curY = 0;
    if (obj.offsetParent) {
        do {
            curX += obj.offsetLeft;
            curY += obj.offsetTop;
        } while (obj = obj.offsetParent);
    return {x:curX,y:curY};
    }
}

function updateCanvas(e){
    var pos = findOffset(canvas);
    
    mouseX = e.pageX - pos.x;
    mouseY = e.pageY - pos.y;
    
    
    ctx.clearRect(0,0,canvas.width,canvas.height);
    drawCanvas();
}

function popBubble(){
    
    for (var i = 0; i < bubbles.length; i++) {
        if(bubbles[i].mouse == true){
        
            bubbles.splice(i,1);
            break;
            
        }
    }
    
    ctx.clearRect(0,0,canvas.width,canvas.height);
    drawCanvas();
}


function drawCanvas() {
    var bgGradient = ctx.createLinearGradient(0,0,0,canvas.height);
    bgGradient.addColorStop(0,'rgb(0,150,255)');
    bgGradient.addColorStop(1,'rgb(0,50,55)');
    
    ctx.fillStyle = bgGradient;
    ctx.fillRect(0,0,canvas.width,canvas.height);
    
    for(var i = 0; i < bubbles.length; i++){
        
        var gradient = ctx.createRadialGradient(bubbles[i].x-bubbles[i].r/3,bubbles[i].y-bubbles[i].r/2,
                0,bubbles[i].x,bubbles[i].y,bubbles[i].r);
        gradient.addColorStop(.2,'rgba(0,150,200,.2)');
        gradient.addColorStop(1,'rgba(255,255,255,.5)');
        
        ctx.beginPath();
        ctx.fillStyle = gradient;
        ctx.arc(bubbles[i].x,bubbles[i].y,bubbles[i].r,0,Math.PI*2,false);
        if(ctx.isPointInPath(mouseX,mouseY)){
            bubbles[i].mouse = true;     
        } else {
            bubbles[i].mouse = false;
        }
        
        ctx.fill();
    }
}