Are there any open source libraries (JaveScript) that implement even-odd fill rule on canvas.
If I try to implement it myself then how complex would it be (considering general case which has complex curves also) and would it hit on the performance (due to overhead of doing it for each pixel in JaveScript).
What are the methods for converting even-odd fill to non-zero winding (considering a generic solution that will work for every case). Once method I found was to divide the shape into all non-intersecting polygons and fill them separately.
One option is to use SVG and draw the SVG on canvas, but I also found that native SVG rendering is a bit slow on iPad, but is SVG slow even when I draw it on HTML canvas (on iPad)?
Thanks in advance
I came across this question as I was looking for the same some time ago. That is, use a "evenodd" fill-rule inside a HTML canvas.
After a little research, and going through mailing lists and patches, I noticed that, as it turns out, recent versions of both Firefox and Chrome have support for this, but each in a different, browser-specific, way.
In Firefox it is a matter of using the mozFillRule attribute. Example:
//
// Firefox Example
//
// canv has the HTML canvas element
var ctx = canv.getContext("2d");
ctx.fillStyle = "#ff0000";
ctx.mozFillRule = 'evenodd';
ctx.beginPath();
ctx.moveTo(100, 10);
ctx.lineTo(40, 180);
ctx.lineTo(190, 60);
ctx.lineTo(10,60);
ctx.lineTo(160,180);
ctx.closePath();
ctx.fill();
In Chrome, you do it by passing the string evenodd as a parameter to the fill method. Example:
//
// Chrome Example
//
// canv has the HTML canvas element
var ctx = canv.getContext("2d");
ctx.fillStyle = "#ff0000";
ctx.beginPath();
ctx.moveTo(100, 10);
ctx.lineTo(40, 180);
ctx.lineTo(190, 60);
ctx.lineTo(10,60);
ctx.lineTo(160,180);
ctx.closePath();
ctx.fill('evenodd');
These are the two browsers I researched for, so I don't know about the state of this in other browers. Hopefully in the not-so-distant future we will be able to use this feature via the fillRule attibute that is now part of the HTML standard.
See fill() method API:
void ctx.fill();
void ctx.fill(fillRule);
void ctx.fill(path, fillRule);
fillRule can be "nonzero" or "evenodd"
--- "nonzero": The non-zero winding rule, which is the default rule.
--- "evenodd": The even-odd winding rule.
Browser compatibility:
--- IE 11+, Firefox 31+, Chrome are OK.
--- I didn't test on IE 9/10.
--- Use ctx.mozFillRule = 'evenodd'; with old Firefox 30/30-.
Related
I was trying out a simple script to create a rectangle and it seemed to work find except that the final leg of the polygon doesn't connect with the first leg properly. The strange thing is that all the other legs do connect properly.
Before anyone suggests that I use the built-in drawRect() or similar function, I should emphasize that this is just a simple example of a far more complex figure I'm trying to draw. I'm just confused why this is happening.
newX = 10;
letterHeight = 100;
ctx.strokeStyle = "#999999";
ctx.lineWidth = 6;
ctx.moveTo(newX,letterHeight*0.5);
ctx.lineTo(newX+letterWidth,letterHeight*0.5);
ctx.lineTo(newX+letterWidth,letterHeight);
ctx.lineTo(newX, letterHeight);
ctx.lineTo(newX, letterHeight*0.5);
ctx.stroke();
It seems to me that this probably has something to do with the line width and I could consider some problem-specific crude fix for that but is there a more general fix if that's the case?
Link to how the script rendered (using firefox)
Use ctx.closePath(); just before the stroke command to close the path. You should also be starting the path with ctx.beginPath();
This question already has answers here:
How to clear the canvas for redrawing
(25 answers)
Closed 8 years ago.
It's a preety straight up and basic question. I think that because that must be a often action there must be made function, but I can't find it? Is there one? If there isn't does anybody know how to do it?
three ways (at least) :
you can clip your canvas : only the non clipped part will be drawn.
to clip you build a path with beginPath, moveTo, lineTo, arcTo, ... arc,
(any path building function) then you call clip() : the part inside the path
is the clipped-in part.
do not forget to save the context before and to restore it after (unless you want a permanent clip).
snippet :
http://jsbin.com/oSoSugAZ/1/
var context=document.getElementById('cv').getContext('2d');
context.fillStyle='#F66';
context.fillRect(150,150, 500,500); // draw big rect in red
context.save();
context.lineWidth=0; // to have precise clip
context.beginPath();
context.moveTo(100,100);
context.lineTo(200,100);
context.lineTo(200,200);
context.lineTo(100,200);
context.closePath();
context.clip();
// here we can only draw within the rect (100,100) (200,200)
context.fillStyle='#FCE'; // pink
context.fillRect(150,150, 500,500); // will get clipped
context.beginPath();
context.fillStyle = '#AAF';
context.arc(150,150, 30, 0,6.2);
context.fill(); // won't get clipped
// do not forget to restore !
context.restore();
you might use globalCompositeOperation to choose a way the pixel you draw interact with existing ones It works for both drawing paths or images. There are too many options to discuss here, it all depend on your needs.
a full example is here :
https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
Note that darker does not work (but it's not usefull anyway, just use normal mode = source-over with low opacity (0.2) and fillRect in black).
another option is to use temporary canvas to make your draws.
It's very easy, especially if you make small helper functions.
function createCanvas(w,h){
var cv = document.createElement('canvas');
cv.width; cv.height = height;
return cv;
}
( in any case you're interested, you can have a look at my small lib to ease working with a canvas :
https://github.com/gamealchemist/CanvasLib/blob/master/canvasLib.js )
You can't. But you can simulate layers with multiple canvas elements with different z-index.
EDIT:
Check this: canvas layers
I am having difficulties rendering several patterns (each with different texture) in the 2d context of HTML5 canvas.
Assuming I have three separate canvases, two off-screen containing different textures and one for rendering. Let these offline canvases be A and B.
Then:
var patternA = ctx.createPattern(A, "repeat-x");
ctx.fillStyle = patternA;
ctx.fillRect(100,100,20,20);
var patternB = ctx.createPattern(B, "repeat-y");
ctx.fillStyle = patternB;
ctx.fillRect(150,100,20,20);
There should be two 20x20 rectangles, each with their own pattern, however the second rectangle doesn't render at all. I've tried everything to get them working, but to no avail.
Why is that? How should I render multiple tiling textures onto the same canvas?
What browsers are you trying? With FireFox and Chrome, I couldn't get either pattern to render with repeat-x or repeat-y. Instead, I was able to get both to render with just repeat. (See http://jsfiddle.net/ZthsS/1/)
It is possible that browsers have an incomplete implementation of the specification. According to the implementation status at http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-createpattern, IE beta and FF nightly pass all test cases but other browsers don't. I would recommend just using repeat for the time being. You could emulate repeat-x and repeat-y by simply limiting the width of the fillRect to the width of the pattern:
var patternA = ctx.createPattern(A, "repeat");
ctx.fillStyle = patternA;
ctx.fillRect(100,100,20,Math.min(20, A.height));
var patternB = ctx.createPattern(B, "repeat");
ctx.fillStyle = patternB;
ctx.fillRect(150,100,Math.min(20, B.width), 20);
I have tried to use globalCompositeOperation in a loop passing it different
string (source-atop, source-over etc.) in the same 2D context but I noticed
that Firefox let draw me only few shapes while Opera only the last.
Now, my question is can I use only ONE globalCompositeOperation at time into
the current context?
The reason you're noticing this issue is the modes you're choosing aren't supported by the browser properly. There are some issues between browsers concerning the globalCompositeOperation. At this moment, there are only a few modes that work between browsers (Chrome/Safari/Opera/Firefox) without quirks:
source-over
source-atop
destination-over
destination-out
lighter
xor
To learn more check out the following link;
http://www.rekim.com/2011/02/11/html5-canvas-globalcompositeoperation-browser-handling/
As for your 2nd question, you can only use one mode at a time. This is unfortunate, because "light" and "darker" are more-like "blend-modes", and would be very useful to use with some of the other composite modes. I would love to see this change.
In short, yes.
The last globalCompositeOperation value takes place before a render, e.g. drawImage(),fillRect().
You can change it immediately after drawing to apply it to the next drawing like:
http://jsfiddle.net/eCDRN/
ctx.globalCompositeOperation = "copy";
ctx.fillRect(100, 100, 100, 100);
ctx.globalCompositeOperation = "destination-in";
ctx.fillRect(150, 150, 100, 100);
ctx.globalCompositeOperation = "xor";
ctx.fillRect(175, 175, 100, 100);
Does anyone know if it's possible, either natively or mathematically with JavaScript, to implement the evenodd fill-rule from SVG in the HTML5 canvas element? Links to projects that do this would be helpful.
Yes, it should be possible with globalCompositeOperation. If I'm not mistaken, default "source-over" value should correspond to SVG's "evenodd" (otherwise, try few others one and see if resulting image looks identical).
By mathematically do you mean an algorithm implementation ? It is certainly possible, see http://gpolo.awardspace.info/fill/main.html. It is more a demo than anything, but it solves this issue "mathematically".
I asked myself the same question and came across this Mozilla Bugreport.
Seems as it gets proposed to the WHATWG (canvas specs) by the bugreporters:
Chris Jones, 2011-06-10:
Let's wait for docs until this spec is looking solid on whatwg (I'll post today).
That's a really old question, so things must have been different at the time, but since a long time ago, you can pass an fillrule parameter to the fill() method.
This fillrule can be either "nonzero", the default, or "evenodd".
var ctx = c.getContext('2d');
drawPath();
ctx.fill();
ctx.translate(70, 0);
drawPath();
ctx.fill('evenodd');
ctx.translate(70, 0);
drawPath();
ctx.stroke();
function drawPath(){
ctx.beginPath();
ctx.arc(30,30,20,0,Math.PI*2);
ctx.lineTo(60,60);
ctx.lineTo(0,0);
ctx.closePath();
}
<canvas id="c"></canvas>