Search code examples
javascriptnode.jscanvasdiscord.jsnode-canvas

Overlapping clipping issue


I have an issue where I am grabbing a users displayAvatar() and then I use an arc to make the image round. This works fine, however, I need to place a circle on top of that image, but it is getting cut in half because of the previous clip()

Without clip() : https://i.gyazo.com/b474c656f33a1f004f5e3becffcef527.png

With clip() : https://i.gyazo.com/da13377cd3f6dc7516c2b8fd1f0f8ac9.png

I know that in the 'With clip()' image, it appears as if the arc border is showing outside of the clip, but that is hardcoded into the image, I put it as a guide with an image editor.

I tried moving around the code, I removed the line ctx.clip() and saw that my circle displays fine on top of the image.

        // circle around avatar
        ctx.beginPath();
        ctx.arc(122.5, 141.8, 81, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.clip();
        const avatar = await Canvas.loadImage(
            message.author.displayAvatarURL({ format: 'png' })
        );
        ctx.strokeStyle = '#ffffff';
        ctx.strokeRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(avatar, 41.5, 60.5, 162, 162);

        // presence circle
        ctx.beginPath();
        ctx.arc(184.5, 193.5, 19, 0, Math.PI * 2, true);
        ctx.strokeStyle = '#000000';
        ctx.lineWidth = 8;
        ctx.stroke();
        ctx.fillStyle = userStatusColor;
        ctx.fill();

Solution

  • Take a look at the canvas clip() definition:
    https://www.w3schools.com/tags/canvas_clip.asp

    Tip: Once a region is clipped, all future drawing will be limited to the clipped region (no access to other regions on the canvas). You can however save the current canvas region using the save() method before using the clip() method, and restore it (with the restore() method) any time in the future.

    Below is an example using the save and restore

    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    
    ctx.beginPath();
    ctx.arc(90, 90, 81, 0, Math.PI * 2, true);
    ctx.stroke();
    
    ctx.save();
    ctx.clip();
    
    ctx.beginPath();
    ctx.arc(150, 50, 19, 0, Math.PI * 2, true);
    ctx.fillStyle = '#0000ff';
    ctx.lineWidth = 8;
    ctx.stroke();
    ctx.fill();
    
    ctx.restore();
    
    ctx.beginPath();
    ctx.arc(170, 99, 19, 0, Math.PI * 2, true);
    ctx.fillStyle = '#ff0000';
    ctx.lineWidth = 8;
    ctx.stroke();
    ctx.fill();
    <canvas id="canvas"></canvas>