Detection on clicking bezier path shape with HTML5

To detect clicks on Bezier path shapes in HTML5 Canvas, you need to use pixel-based detection since Canvas doesn't have built-in shape hit testing. This technique draws the shape invisibly and checks if the clicked pixel contains color data.

How Pixel-Based Detection Works

The method involves drawing the Bezier shape to a hidden canvas context, then checking if the clicked coordinates contain any pixels. If the alpha channel is greater than 0, the click hit the shape.

Complete Click Detection Example

<canvas id="myCanvas" width="400" height="300"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// Sample Bezier shapes array
const boxes = [
    {x: 50, y: 50, width: 100, height: 80, type: 'bezier'},
    {x: 200, y: 100, width: 120, height: 90, type: 'bezier'}
];

let mySel = null;
let isDrag = false;
let offsetx = 0, offsety = 0;

// Draw a sample Bezier shape
function drawshape(context, shape, strokeColor, fillColor) {
    context.beginPath();
    context.moveTo(shape.x, shape.y);
    context.bezierCurveTo(
        shape.x + shape.width/2, shape.y - 20,
        shape.x + shape.width/2, shape.y + shape.height + 20,
        shape.x + shape.width, shape.y + shape.height
    );
    context.lineTo(shape.x, shape.y + shape.height);
    context.closePath();
    
    context.fillStyle = fillColor;
    context.fill();
    context.strokeStyle = strokeColor;
    context.stroke();
}

// Click detection function
canvas.addEventListener('click', function(e) {
    const rect = canvas.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;
    
    // Create hidden context for hit testing
    const hiddenCanvas = document.createElement('canvas');
    hiddenCanvas.width = canvas.width;
    hiddenCanvas.height = canvas.height;
    const gctx = hiddenCanvas.getContext('2d');
    
    // Check each shape (reverse order for top-most selection)
    const l = boxes.length;
    for (let i = l-1; i >= 0; i--) {
        gctx.clearRect(0, 0, hiddenCanvas.width, hiddenCanvas.height);
        drawshape(gctx, boxes[i], 'black', 'black');
        
        const imgData = gctx.getImageData(mx, my, 1, 1);
        
        // Check if pixel has content (alpha > 0)
        if (imgData.data[3] > 0) {
            mySel = boxes[i];
            console.log('Selected shape:', mySel);
            redrawCanvas();
            return;
        }
    }
    
    mySel = null;
    redrawCanvas();
});

function redrawCanvas() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    boxes.forEach(box => {
        const isSelected = box === mySel;
        drawshape(ctx, box, isSelected ? 'red' : 'black', 
                 isSelected ? 'lightblue' : 'lightgray');
    });
}

// Initial draw
redrawCanvas();
</script>

Key Implementation Details

The detection algorithm works by:

  • Hidden Canvas: Creates an invisible canvas for hit testing without affecting the display
  • Reverse Loop: Iterates through shapes backwards to select the topmost overlapping shape
  • Alpha Channel Check: Uses imgData.data[3] > 0 to detect if the pixel contains shape data
  • Coordinate Mapping: Converts mouse coordinates relative to canvas position

Enhanced Version with Drag Support

<canvas id="dragCanvas" width="400" height="300"></canvas>
<script>
const dragCanvas = document.getElementById('dragCanvas');
const dragCtx = dragCanvas.getContext('2d');

const shapes = [{x: 100, y: 100, width: 80, height: 60, type: 'bezier'}];
let selectedShape = null;
let isDragging = false;
let dragOffsetX = 0, dragOffsetY = 0;

function drawBezierShape(ctx, shape, stroke, fill) {
    ctx.beginPath();
    ctx.moveTo(shape.x, shape.y);
    ctx.bezierCurveTo(
        shape.x + shape.width/3, shape.y - 15,
        shape.x + 2*shape.width/3, shape.y + shape.height + 15,
        shape.x + shape.width, shape.y + shape.height/2
    );
    ctx.lineTo(shape.x, shape.y + shape.height);
    ctx.closePath();
    
    ctx.fillStyle = fill;
    ctx.fill();
    ctx.strokeStyle = stroke;
    ctx.stroke();
}

dragCanvas.addEventListener('mousedown', function(e) {
    const rect = dragCanvas.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;
    
    // Hit detection
    const testCanvas = document.createElement('canvas');
    testCanvas.width = dragCanvas.width;
    testCanvas.height = dragCanvas.height;
    const testCtx = testCanvas.getContext('2d');
    
    for (let i = shapes.length - 1; i >= 0; i--) {
        testCtx.clearRect(0, 0, testCanvas.width, testCanvas.height);
        drawBezierShape(testCtx, shapes[i], 'black', 'black');
        
        const imgData = testCtx.getImageData(mx, my, 1, 1);
        if (imgData.data[3] > 0) {
            selectedShape = shapes[i];
            dragOffsetX = mx - selectedShape.x;
            dragOffsetY = my - selectedShape.y;
            isDragging = true;
            break;
        }
    }
    
    redrawShapes();
});

dragCanvas.addEventListener('mousemove', function(e) {
    if (!isDragging || !selectedShape) return;
    
    const rect = dragCanvas.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;
    
    selectedShape.x = mx - dragOffsetX;
    selectedShape.y = my - dragOffsetY;
    
    redrawShapes();
});

dragCanvas.addEventListener('mouseup', function() {
    isDragging = false;
});

function redrawShapes() {
    dragCtx.clearRect(0, 0, dragCanvas.width, dragCanvas.height);
    
    shapes.forEach(shape => {
        const isSelected = shape === selectedShape;
        drawBezierShape(dragCtx, shape, 
                       isSelected ? 'red' : 'blue',
                       isSelected ? 'yellow' : 'lightgreen');
    });
}

redrawShapes();
</script>

Browser Compatibility

This technique works in all modern browsers that support HTML5 Canvas and getImageData(). The method is reliable across Chrome, Firefox, Safari, and Edge.

Conclusion

Pixel-based hit detection provides accurate click detection for complex Bezier shapes in Canvas. Use hidden canvas contexts for testing and check alpha channel values to determine if a click intersects with your shapes.

Updated on: 2026-03-15T23:18:59+05:30

262 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements