Canvas 'Clip' reverse action?
Asked Answered
T

2

6

Assuming I have:

var context = document.getElementById('test').getContext('2d');

// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);

// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();
context.closePath();

context.globalCompositeOperation = 'destination-out';

// 'P' hole
context.beginPath();
context.moveTo(124,117);
context.lineTo(146,117);
context.quadraticCurveTo(160,117,160,127);
context.quadraticCurveTo(160,145,146,145);
context.lineTo(120,145);
context.lineTo(124,117);
context.fillStyle = '#ffffff';
context.fill();
context.closePath();

This works, except as you can see the 'hole' isn't a clip, so if the background isn't solid, you simply can't set the 'fill' color of the hole to match the background.

Therefore I need to clip the hole instead. When I do that however, the only part of the 'P' that shows is the part bound by the clip 'hole'. I need the reverse. I need the 'P' to show, but clip the part with the 'hole' so any background will show through.

Here is as far as I got, but not quite there:

var context = document.getElementById('test').getContext('2d');

// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);

// 'P' hole clip
context.beginPath();
context.moveTo(124,117);
context.lineTo(146,117);
context.quadraticCurveTo(160,117,160,127);
context.quadraticCurveTo(160,145,146,145);
context.lineTo(120,145);
context.lineTo(124,117);
context.clip();
context.closePath();

// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();
context.closePath();

Thank you for your help!

Tequilater answered 27/1, 2011 at 20:49 Comment(0)
B
7

I understand you asked this a while ago but I have an answer for you.

Your first example was half-correct. Using destination-out will work, however in order not to disturb the canvas you want to draw on, we create a new canvas and draw it in that.

Then once we've drawn our shape on there with our cutaways, we then draw the entire canvas onto the original. Since the new canvas has no background it will keep transparency.

var canvas = document.getElementById('test'),
    context = canvas.getContext('2d'),

    // New canvas - we will draw the letter P on here
    newCanvas = document.createElement('canvas'),
    newContext = newCanvas.getContext('2d');

// Make sure you have enough room on your new canvas
newCanvas.width = canvas.width;
newCanvas.height = canvas.height;

with(newContext) {
    // 'P'
    beginPath();
    moveTo(90,89);
    lineTo(161,89);
    quadraticCurveTo(200,89,200,127);
    quadraticCurveTo(200,166,148,166);
    lineTo(115,166);
    lineTo(108,210);
    lineTo(69,210);
    lineTo(90,89);
    fillStyle = "#eee";
    fill();
    closePath();

    globalCompositeOperation = 'destination-out';

    // 'P' hole
    beginPath();
    moveTo(124,117);
    lineTo(146,117);
    quadraticCurveTo(160,117,160,127);
    quadraticCurveTo(160,145,146,145);
    lineTo(120,145);
    lineTo(124,117);
    fillStyle = '#000';
    fill();
    closePath();
}

with(context) {
    // Background
    fillStyle = '#000';
    fillRect(0,0,300,300);

    // Simply reference the canvas element when drawing
    drawImage(newCanvas, 0, 0);
}
Briareus answered 25/4, 2011 at 8:32 Comment(0)
J
0

I know this is very old but... you are not using clip correctly in your second try. Everything that comes after clip will be drawn only on the clipped area.

so you could either:

  • Clip the hole and redraw the background

var context = document.getElementById('test').getContext('2d');

// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);


// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();


// 'P' hole clip
context.beginPath();
context.moveTo(124,117);
context.lineTo(146,117);
context.quadraticCurveTo(160,117,160,127);
context.quadraticCurveTo(160,145,146,145);
context.lineTo(120,145);
context.lineTo(124,117);
context.clip();

// Redraw Background in the clipped area
context.fillStyle = '#000';
context.fillRect(0,0,300,300);
<canvas id="test" width="300" height="300">
  • clip the outside part of the hole and draw the P

var context = document.getElementById('test').getContext('2d');

// Background
context.fillStyle = '#000';
context.fillRect(0,0,300,300);

// inverse 'P' hole clip
context.beginPath();
context.rect(0, 0, 300, 300);
context.lineTo(124,117);
context.lineTo(120,145);
context.lineTo(146,145);
context.quadraticCurveTo(160,145,160,127);
context.quadraticCurveTo(160,117,146,117);
context.lineTo(124,117);
context.clip();

// 'P'
context.beginPath();
context.moveTo(90,89);
context.lineTo(161,89);
context.quadraticCurveTo(200,89,200,127);
context.quadraticCurveTo(200,166,148,166);
context.lineTo(115,166);
context.lineTo(108,210);
context.lineTo(69,210);
context.lineTo(90,89);
context.fillStyle = "#eee";
context.fill();
<canvas id="test" width="300" height="300">

Also, you don't need to use closePath here.

Jelsma answered 14/3, 2017 at 22:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.