There are several issues here :
• Your mouse code is a nightmare, traversing the DOM on every move.
• You are redrawing everything on each move.
So i suggest a way more efficient solution :
• stack two canvases, the one below is your image, the one on top is the mask.
• Deal efficiently with the mouse.
• Only clear part of the mask canvas on mouse move : just one circle drawn on the mask canvas for each move.
(for that i used a globalCompositeOperation = 'destination-out' )
Result is perfectly smooth either on Firefox, Chrome, or Safari .
(tested on mac OS).
the fiddle :
(you have to click to clear)
http://jsfiddle.net/gamealchemist/4h7GR/22/
html
<canvas style='position: absolute; top: 0;left: 0;' id="canvas1" width="500" height="500"></canvas>
<canvas style='position: absolute;top: 0;left: 0;' id="canvas2" width="500" height="500"></canvas>
js
var can = document.getElementById("canvas1");
var ctx = can.getContext("2d");
var can2 = document.getElementById("canvas2");
var ctx2 = can2.getContext("2d");
var img = new Image();
img.onload = function () { ctx.drawImage(img,0,0); };
img.src = "http://placekitten.com/500/500";
ctx2.fillStyle='#000';
ctx2.fillRect(0,0,500,500);
ctx2.globalCompositeOperation = 'destination-out';
function clearThis(x,y) {
console.log('toto');
ctx2.fillStyle='#F00000';
ctx2.beginPath();
ctx2.arc(x, y, 70, 0, Math.PI * 2, true);
ctx2.fill();
}
var mouse = {
x: 0,
y: 0,
down: false
};
function setupMouse(canvas, onMouseMove, preventDefault) {
var rectLeft, rectTop;
var hook = canvas.addEventListener.bind(canvas);
var mouseDown = updateMouseStatus.bind(null, true);
var mouseUp = updateMouseStatus.bind(null, false);
hook('mousedown', mouseDown);
hook('mouseup', mouseUp);
hook('mousemove', updateCoordinates);
hook('scroll', updateRect);
// var mouseOut = function() { mouse.down=false ; } ;
// hook('mouseout', mouseOut);
function updateMouseStatus(b, e) {
mouse.down = b;
updateCoordinates(e);
if (preventDefault) {
e.stopPropagation();
e.preventDefault();
}
}
function updateCoordinates(e) {
mouse.x = (e.clientX - rectLeft);
mouse.y = (e.clientY - rectTop);
onMouseMove(mouse.x, mouse.y);
}
function updateRect() {
var rect = canvas.getBoundingClientRect();
rectLeft = rect.left;
rectTop = rect.top;
}
updateRect();
};
setupMouse(can2, clearThis, true);