简体   繁体   中英

Image as a stencil for html canvas

I'm trying to have a user manually color in certain parts of an image. As an example, here's a cat https://techflourish.com/images/3-cat-clipart-9.png . The user should be able to color in the foot of the cat if they choose to. I want them to only color inside the cat body image portion of the canvas (not the background portion of the image or whitespace of canvas, but I guess I could just manually trim the image).

What I've attempted so far is below. Basically I check the color of the pixel at my position and draw only if it isn't that background color. This sort of works, but I'm able to bleed out really easily because something is off. I was wondering if it was possibly to set a specific clip area, but wasn't able to figure it out.

`

    var canvas = document.getElementById("space");
    var ctx = canvas.getContext("2d");

    var pos = { x: 0, y: 0 };

    // new position from mouse events
    function setPosition(e) {
        pos.x = e.clientX;
        pos.y = e.clientY;
    }

    function rgbToHex(r, g, b) {
        if (r > 255 || g > 255 || b > 255)
            throw "Invalid color component";
        return ((r << 16) | (g << 8) | b).toString(16);
    }

    function draw(e) {
        if (e.buttons !== 1) return; // if mouse is pressed.....

        var color = "#cb3594";

        ctx.beginPath(); // begin the drawing path

        ctx.lineWidth = 5; // width of line
        ctx.lineCap = "round"; // rounded end cap
        ctx.strokeStyle = color; // hex color of line

        var p = ctx.getImageData(pos.x, pos.y, 1, 1).data;
        var sourceColor = rgbToHex(p[0], p[1], p[2]);
        if(sourceColor != "BACKGROUNDHEX" && sourceColor != color) {
            ctx.moveTo(pos.x, pos.y); // from position
            setPosition(e);
            p = ctx.getImageData(pos.x, pos.y, 1, 1).data;
            targetColor = rgbToHex(p[0], p[1], p[2]);

            if(targetColor != "BACKGROUNDHEX" && targetColor != color) {
                ctx.lineTo(pos.x, pos.y); // to position
                ctx.stroke(); // draw it!
            }
        }

    }

    var outlineImage = new Image();
    outlineImage.onload = function() {
        ctx.drawImage(outlineImage, 0, 0, 704, 720);
    }
    outlineImage.src = "IMAGE.png";

    space.addEventListener("mousemove", draw);
    space.addEventListener("mousedown", setPosition);
    space.addEventListener("mouseenter", setPosition);


</script>

`

(related edit: the bleeding is caused by my "sourceColor != color" being wrong, but the question is still relevant as this still doesn't feel like a great solution)

Since the parts of the image you don't want to color are transparent, you can set the context's globalCompositeOperation to 'source-atop' . After that, any pixels you draw to the canvas will automatically take on the overwritten pixels' opacity, and you don't have to mess with getImageData :

 var canvas = document.getElementById("space"); var ctx = canvas.getContext("2d"); var pos = { x: 0, y: 0 }; // new position from mouse events function setPosition(e) { // offsetX/Y gives the correct coordinates within the canvas // assuming it has no padding pos.x = e.offsetX; pos.y = e.offsetY; } function draw(e) { if (e.buttons !== 1) return; // if mouse is pressed..... var color = "#cb3594"; ctx.beginPath(); // begin the drawing path ctx.lineWidth = 5; // width of line ctx.lineCap = "round"; // rounded end cap ctx.strokeStyle = color; // hex color of line ctx.moveTo(pos.x, pos.y); // from position setPosition(e); ctx.lineTo(pos.x, pos.y); // to position ctx.stroke(); // draw it! } var outlineImage = new Image(); outlineImage.onload = function() { // the default, set explicitly because we're changing it elsewhere ctx.globalCompositeOperation = 'source-over'; ctx.drawImage(outlineImage, 0, 0); // don't draw over the transparent parts of the canvas ctx.globalCompositeOperation = 'source-atop'; // wait until the stencil is loaded before handing out crayons space.addEventListener("mousemove", draw); space.addEventListener("mousedown", setPosition); space.addEventListener("mouseenter", setPosition); } outlineImage.src = "https://i.stack.imgur.com/so095.png"; 
 <canvas id="space" width="610" height="733"></canvas> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM