Question

Okay, we need your help! We (with our informatics class) are building a digital scratchmap! Like this:

https://www.megagadgets.nl/media//ScratchMap3_thumb3.jpg
(source: megagadgets.nl)

With your mouse you should be able to scratch out the places you've been to. Now we're stuck. We have a canvas and we draw the image of a world map. Then when the user clicks and drags a stroke gets add on top of the world map.

Now we want to convert the (green drawn) strokes to transparency so we can reveal the image behind it. (Just like scratching out the places you've been to and revealing the map behind it (in colour)).

This is our html:

<body>
    <h1>Scratchmap</h1>
    <hr>
    <canvas id="ball" width="600px" height ="600px">

    </canvas>

    <canvas id="ball2" width="600px" height ="600px">

    </canvas>
</body>

And this is our javascript:

// Set variables
var a_canvas = document.getElementById("ball");
var context = a_canvas.getContext("2d");

var a_canvas2 = document.getElementById("ball2");
var context2 = a_canvas2.getContext("2d");
var img = new Image();
img.onload = function () {
    context.drawImage(img, img_x, img_y);
}
img.src = "worldmap.png"
var mouse_pos_x = [];
var mouse_pos_y = [];

var thickness = 0;

var arraycount = 0;

var mouse_down = false;

var mouse_skip = [];

function update() {}

document.body.onmousedown = function () {
    mouse_down = true;
    var mouseX, mouseY;

    if (event.offsetX) {
        mouseX = event.offsetX;
        mouseY = event.offsetY;
    } else if (event.layerX) {
        mouseX = event.layerX;
        mouseY = event.layerY;
    }
    mouse_pos_x.push(mouseX);
    mouse_pos_y.push(mouseY);
    arraycount += 1;
}

document.body.onmouseup = function () {
    if (mouse_down) {
        mouse_down = false;
        mouse_skip.push(arraycount);
    }
}

document.body.onmousemove = function () {
    if (mouse_down) {
        var mouseX, mouseY;

        if (event.offsetX) {
            mouseX = event.offsetX;
            mouseY = event.offsetY;
        } else if (event.layerX) {
            mouseX = event.layerX;
            mouseY = event.layerY;
        }

        context.drawImage(img, 0, 0);
        mouse_pos_x.push(mouseX);
        mouse_pos_y.push(mouseY);
        context.lineWidth = 2.5;
        context.strokeStyle = "#00FF00";
        context.moveTo(mouse_pos_x[arraycount - 1], mouse_pos_y[arraycount - 1]);
        context.lineTo(mouse_pos_x[arraycount], mouse_pos_y[arraycount]);
        context.stroke();
        arraycount += 1;

        var imgdata = context.getImageData(0, 0, a_canvas.width, a_canvas.height);
        var l = imgdata.data.length / 4;

        for (var i = 0; i < l; i++) {
            var r = imgdata.data[i * 4 + 0];
            var g = imgdata.data[i * 4 + 1];
            var b = imgdata.data[i * 4 + 2];
            if (g < 255) {
                imgdata.data[i * 4 + 3] = 0;
            }
        }
        context2.putImageData(imgdata, 0, 0);
    }
}

setInterval(update, 10);

Now when we remove the draw_image() the green color becomes yellow on the other canvas. But with the draw_image() nothing gets drawn on the second canvas.

What's going wrong? Or do you have a way to do this with other Javascript or not in javascript at all?

Any help would be appreciated!

Luud Janssen & Friends

Was it helpful?

Solution

You can do this with a slightly different approach:

  • Set the hidden image as CSS background
  • Draw the cover image on top using context
  • Change composite mode to destination-out
  • Anything now drawn will erase instead of draw revealing the (CSS set) image behind

Live demo

The key code (see demo linked above for details):

function start() {
    /// draw top image - background image is already set with CSS
    ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
    
    /// KEY: this will earse where next drawing is drawn
    ctx.globalCompositeOperation = 'destination-out';
    
    canvas.onmousedown = handleMouseDown;
    canvas.onmousemove = handleMouseMove;
    window.onmouseup = handleMouseUp;
}

Then it's just a matter of tracking the mouse position and draw any shape to erase that area, for example a circle:

function erase(x, y) {
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, pi2);
    ctx.fill();
}

Snapshot
Random images for illustrative purposes

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top