Say I have a circle (an arc) on HTML5 canvas. I can just fill it like this:
ctx.arc(100, 100, 50, 0, 2 * Math.PI);
ctx.fill();
It works a treat. However, how can one fill the opposite area? Now it's white with a black circle, but I'd like it to be along the lines of the following image (on which white is the background color and black is the filling color):
I know I could just use a black background and paint a white circle, but the background can be anything (all kinds of things have been drawn there before, so just swapping colors is not possible).
Another thing is that not the complete canvas should be filled, but rather a square with an circle canceled out.
I was thinking of a globalCompositeOperation
but it开发者_如何转开发 does not seem to fit my needs as none of them act according to what I need.
So, how can I accomplish filling the 'opposite' area like in the example image?
Draw the black shape in the OP-- that is, draw a rectangle with a circle cut out of the middle.
First call beginPath(); then draw the circle clockwise; then draw the rectangle counter-clockwise (or vice versa); and finally fill().
var ctx = $('#cv').get(0).getContext('2d');
ctx.fillStyle = "grey";
ctx.beginPath();
ctx.arc(200, 200, 100, 0, 2 * Math.PI);
ctx.rect(400, 0, -400, 400);
ctx.fill();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas width="400" height="400" id="cv"></canvas>
It might not be obvious from most of the documentation out there but you can combine multiple subpaths in a single fill(), stroke() or clip() operation. To make a hole in a shape, you just draw the shape of the hole in the opposite direction. The beginPath() method resets the current path. For fill() and clip(), Canvas uses the non-zero winding number rule-- if you read up on that it should become clearer.
You can do that using another canvas as mask :
// This is the canvas where you want to draw
var canvas = document.getElementById('your-canvas');
var ctx = canvas.getContext('2d');
// I'll use a skyblue background that covers everything
// Just to demonstrate
ctx.fillStyle = "skyblue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Create a canvas that we will use as a mask
var maskCanvas = document.createElement('canvas');
// Ensure same dimensions
maskCanvas.width = canvas.width;
maskCanvas.height = canvas.height;
var maskCtx = maskCanvas.getContext('2d');
// This color is the one of the filled shape
maskCtx.fillStyle = "black";
// Fill the mask
maskCtx.fillRect(0, 0, maskCanvas.width, maskCanvas.height);
// Set xor operation
maskCtx.globalCompositeOperation = 'xor';
// Draw the shape you want to take out
maskCtx.arc(30, 30, 10, 0, 2 * Math.PI);
maskCtx.fill();
// Draw mask on the image, and done !
ctx.drawImage(maskCanvas, 0, 0);
<canvas id="your-canvas">
Demo here.
The way this would typically be done is with a "clipping region", where you make an area mask for which the drawing operations apply. HTML5 canvas has the ability to make a clipping mask that is the circle itself...
http://www.html5canvastutorials.com/tutorials/html5-canvas-clipping-region-tutorial/
...but it lacks the subtract() operation that's in Google Gears Canvas, which I spotted and they claimed is "from HTML5"...but apparently it is not.
Someone answered how to invert a clip() here, but it involves an off-screen canvas:
Canvas 'Clip' reverse action?
Hopefully someone else will have a better suggestion. :-/
Using the XOR function on a canvas mask only works with opaque fills (i.e. not compatible with globalAlpha). One way around this is to use the .clip() function and filling the clipped region with what you had in the background. Then, you can overlay the clipped region on top of the original canvas, on which you performed whatever fill you needed to.
精彩评论