html5 canvas - merge two clipping regions - James Bond Gunbarrel - javascript

I have just started using canvas to get this kind of effect:
"James Bond Gunbarrel View Start"
http://www.youtube.com/watch?v=sfXazFp68cE
I managed to get almost there:
http://jsbin.com/uyaker/8/edit
Now as you can see I clip my canvas with two circles (At least I try to) ... but the problem is the region that overlaps is not clipped anymore ...
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(canvas.width, 0);
ctx.lineTo(canvas.width, canvas.height);
ctx.lineTo(0, canvas.height);
ctx.closePath();
ctx.moveTo(cx + r, cy);
ctx.arc(cx, cy, r, 0, Math.PI*2, true);
// check if to draw a fixed circle, every 200 pixels for 100 pixels
if(goingright && cx % 200 < 100) {
ctx.moveTo(cx - cx % 200 + r, cy);
ctx.arc(cx - cx % 200, cy, r, 0, Math.PI*2, true);
}
ctx.closePath();
ctx.clip();
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
Maybe this effect is possible without using clipping but give the canvas a css background-image and draw everything but the circles ... but I don't really know how to do that :/.

Maybe this effect is possible without using clipping ... but I don't really know how to do that
Yes it is possible, you need to create a path that will do all the work:
context.beginPath();
context.moveTo(x, y);
// Draw your big shape, in your case is a rectangle (4 point)
context.lineTo(xn, yn);
context.closePath();
// Now the context knows that every path that will be added without .beginPath(), will clip the current path
context.arc(cx, cy, r, 0, Math.PI * 2, true);
context.closePath();
context.fill(); // Fill with color all the area except the arc
Example: http://jsfiddle.net/drhWb/
Saving, restoring and clipping the context are very expensive operations so you should use this approach is the right way you need to go.

Related

how to manage zoom and length handle in papaya

What I want to do is
1) I want to draw a length on image and want to add handle to edit it.
2) When I want to zoom the image the drawn length should match the zoom level.
ctx.beginPath();
ctx.moveTo(linearr[i].x1, linearr[i].y1);
ctx.lineTo(linearr[i].x2, linearr[i].y2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.arc(linearr[i].x1, linearr[i].y1, 2, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = 'green';
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(linearr[i].x2, linearr[i].y2, 2, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = 'green';
ctx.fill();
ctx.stroke();
where linearr is an array
Papaya already supports a ruler tool, if that's what you're looking for.
Otherwise, see the function papaya.viewer.Viewer.prototype.drawRuler() for an example of how to use the screen transform to draw lines, which includes the zoom transform. Another function that might be helpful to you is this.selectedSlice.findProximalRulerHandle().

HTML5 Canvas not clipping correctly?

I'm wondering if the following code yields appropriate behavior. I feel like the top left square should still be green. that is, if I clip one area, ten restore, then paint in a second area, the canvas paints in BOTH areas. Why?
https://jsfiddle.net/s6t8k3w3/
var my_canvas = document.getElementById('canvas');
var ctx = my_canvas.getContext("2d");
//Make Clipping Path 1
ctx.save();
ctx.rect(20, 20, 100, 100);
ctx.clip();
//Fill Background with Green
ctx.fillStyle = 'rgba(0,255,0,1)';
ctx.fillRect(0, 0, my_canvas.width, my_canvas.height);
//Close Clipping Path 1
ctx.restore();
//Open Clipping Path 2
ctx.save();
ctx.rect(50, 50, 100, 100);
ctx.clip();
//Fill background with Blue
ctx.fillStyle = 'rgba(0,0,255,1)';
ctx.fillRect(0, 0, my_canvas.width, my_canvas.height);
//Draw Line
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(500, 500);
ctx.stroke();
//CloseClipping Path 2
ctx.restore();
You're not actually opening a second clipping path with that second ctx.save(); to do that, you need to call ctx.beginPath()

HTML5 Canvas Clip using Arc is clipping wrong

So I have tried clipping an image using ctx.clip and ctx.arc like this:
ctx.beginPath();
ctx.arc(250, 250, 250, -Math.PI / 4, Math.PI / 4);
ctx.clip();
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
As you see I have set the start angle to -45 deg and the end angle to 45 deg, but what I get is a cut of circle/half moon, not a pac-man figure, as you would see if you filled the arc using ctx.fill.
FIDDLE
Why is this and how do I fix it?
Thanks in advance.
Not knowing anything about HTML5 or JavaScript, I think you are just not closing the path properly. Add ctx.lineTo(250,250); after the arc: http://jsfiddle.net/7em21gvk/
Okay, so I found the problem, simply add ctx.moveTo(x, y) where x and y is the center of the arc, like this:
ctx.beginPath();
ctx.moveTo(250, 250);
ctx.arc(250, 250, 250, -Math.PI / 4, Math.PI / 4);
ctx.clip();
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
FIDDLE

How can we take advantage of moveTo( ) HTML5 method?

please I am a little confused so I need your help .
My question is how can we take advantage of moveTo() html5 method ?
for example I found this example on stackOverflow
function drawSmile(ctx, x, y, faceRadius, eyeRadius) {
ctx.save(); // save
ctx.fillStyle = '#FF6'; // face style : fill color is yellow
ctx.translate(x, y); // now (x,y) is the (0,0) of the canvas.
ctx.beginPath(); // path for the face
ctx.arc(0, 0, faceRadius, 0, 6.28);
ctx.fill();
ctx.fillStyle = '#000'; // eye style : fill color is black
ctx.beginPath(); // path for the two eyes
ctx.arc(faceRadius / 2, - faceRadius /3, eyeRadius, 0, 6.28);
ctx.moveTo(-faceRadius / 2, - faceRadius / 3); // sub path for second eye
ctx.arc(-faceRadius / 2, - faceRadius / 3, eyeRadius, 0, 6.28);
ctx.fill();
ctx.restore(); // context is just like before entering drawSmile now.
}
drawSmile(c, 200,200, 60, 12);
but when I removed the line number 11 in the code which uses the moveTo method no thing changed!!!!.
The moveTo() HTML5 method lets you to move your (0,0) origin to another point in the space.
Here you have and example. To draw some kind of triangle:
// first part of the path
ctx.moveTo(20,20);
ctx.lineTo(100, 100);
ctx.lineTo(100,0);
// second part of the path
ctx.moveTo(120,20);
ctx.lineTo(200, 100);
ctx.lineTo(200,0);
// indicate stroke color + draw the path
ctx.strokeStyle = "#0000FF";
ctx.stroke();
In this example we simply called moveTo(x, y) after drawing the first part of the path (the shape on the left). Then, we only called stroke() once to draw the whole path.

globalCompositeOperation and concentric, hollow, moving shapes

I'm trying to achieve the following:
A number of concentric circles (or rings) are drawn on a canvas. Each circle has a "hole" in it, so the smaller circles, drawn behind it are partially visible. Each frame (we're using window.requestAnimationFrame to render) the radius of each circle/shape/ring is slightly increased.
A scenario with two rings is depicted in the image here.
The code:
function draw() {
drawBgr();
for (var i = 0, len = rings.length; i < len; i++) {
rings[i].draw();
}
}
function drawBgr() {
context.globalCompositeOperation = "source-over";
context.clearRect(0, 0, WIDTH, HEIGHT);
context.rect(0, 0, WIDTH, HEIGHT);
context.fillStyle = '#FFFFFF';
context.fill();
}
function squareRing(ring) { //called by rings[i].draw();
context.globalCompositeOperation = "source-over";
context.fillRect(ring.centerX - ring.radius / 2, ring.centerY - ring.radius / 2, ring.radius, ring.radius);
context.globalCompositeOperation = "source-out";
context.beginPath();
context.arc(CENTER_X, CENTER_Y, ring.radius, 0, 2 * Math.PI, false);
//context.lineWidth = RING_MAX_LINE_WIDTH * (ring.radius / MAX_SIDE);
context.fillStyle = '#000000';
context.fill();
context.globalCompositeOperation = "source-over";
}
What exactly is the problem here? I'm calling clearRect before the circles are drawn. See "What I'm actually getting" image. This is the result of a SINGLE RING being drawn over a number of frames. I shouldn't be getting anything different than a black circle with a hollow square in the middle. (Note that radius is increasing each frame.)
I do realize switching globalCompositeOperation might not suffice for the effect I desire. How can I draw a "hole" in an object drawn on the canvas without erasing everything in the "hole" underneath the object I'm trying to modify?
This is the tutorial I used as a reference for the globalCompositeOperation values.
I'm using Firefox 28.0.
I would not try to use globalCompositeOperation, since i find it hard to figure out what will happen after several iterations, and even harder if the canvas was not cleared before.
I prefer to use clipping, which gets me to that :
http://jsbin.com/guzubeze/1/edit?js,output
So, to build a 'hole' in a draw, how to use clipping ?
-->> Define a positive clipping sub-path, and within this area, cut off a negative part, using this time a clockwise sub-path :
Clipping must be done with one single path, so rect() cannot be used : it does begin a path each time, and does not allow to choose clockwisity (:-)), so you have to define those two functions which will just create the desired sub-paths :
// clockwise sub-path of a rect
function rectPath(x,y,w,h) {
ctx.moveTo(x,y);
ctx.lineTo(x+w,y);
ctx.lineTo(x+w,y+h);
ctx.lineTo(x,y+h);
}
// counter-clockwise sub-path of a rect
function revRectPath(x,y,w,h) {
ctx.moveTo(x,y);
ctx.lineTo(x,y+h);
ctx.lineTo(x+w,y+h);
ctx.lineTo(x+w,y);
}
then you can write your drawing code :
function drawShape(cx, cy, d, scale, rotation) {
ctx.save();
ctx.translate(cx,cy);
scale = scale || 1;
if (scale !=1) ctx.scale(scale, scale);
rotation = rotation || 0;
if (rotation) ctx.rotate(rotation);
// clip with rectangular hole
ctx.beginPath();
var r=d/2;
rectPath(-r,-r, d, d);
revRectPath(-0.25*r,-0.8*r, 0.5*r, 1.6*r);
ctx.closePath();
ctx.clip();
ctx.beginPath();
// we're clipped !
ctx.arc(0,0, r, 0, 2*Math.PI);
ctx.closePath();
ctx.fill();
ctx.restore();
}
Edit :
For the record, there is a simpler way to draw the asked scheme : just draw a circle, then draw counter clockwise a rect within. What you fill will be the part inside the circle that is outside the rect, which is what you want :
function drawTheThing(x,y,r) {
ctx.beginPath();
ctx.arc(x ,y, r, 0, 2*Math.PI);
revRectPath(x-0.25*r, y-0.8*r, 0.5*r, 1.6*r);
ctx.fill();
ctx.closePath();
}
(i do not post image : it is the same).
Depending on your need if you change the draw or if you want to introduce some kind of genericity, use first or second one.
If you do not change the scheme later, the second solution is simpler => better.

Categories