I have a canvas animation that sometimes redraws the exact same element over another (it is a long story why this is necessary) but it happens, and it happens often enough.
Now I assumed that drawing an element (using a context path and the stroke method) over an existing exact replica of the image should do nothing at all to my animation. Instead it draws over the past image and blurs all the sides (as if it didn't exactly draw at the same location.
Please let me know if there is a way to fix this
Thanks
This is happening because canvas strokes are anti-aliased. In a practical sense you're drawing some semi-opaque pixels over some other semi-opaque pixels, and where the pixels are overlaid their opacities are added together. I don't think you can (currently) force the canvas object to turn off anti-aliasing on strokes, so you may just have to live with it. Is it that big of a problem?
http://img813.imageshack.us/img813/303/canvasl.png
EDIT: I guess you could try using PNGs with transparent backgrounds for your markers, instead of drawing them with strokes. https://developer.mozilla.org/en/Canvas_tutorial/Using_images
Related
The answer to this stackoverflow question effectively says to retrace the steps taken to draw a rectangle:
fillRect
by doing:
clearRect
First of all when I read this answer, I thought to just draw a white box the entire width and height of the canvas.
After thinking a bit more about how the Canvas element can save, and restore and the fact it implements a clearRect, does this mean that simply drawing another large rectangle could eat resources, if doing something like redrawing an entire bar graph every 100ms.
Originally drawing a bar graph every 100ms drew bars on top of each other, thus not being able to see the new bars because they're being layered.
But now, drawing a white rectangle as well, means that many rectangles are getting drawn, layered and it seems that the Canvas element tracks these?
Does this mean that it is possible to effectively overload the element, or overload the browser?
the canvas does not track the drawing operations, but it tracks states. And the color values of individual pixels. If you draw a red rectangle you actually set the state to "draw red" and then set a rectangular shaped area of pixels to the currently drawn color (there are also different other drawing operations than just draw red on-top but I don't have experience with the so I can't tell you much other than that they exist)
For performance reasons you want to (among many other things) minimize
the amount of pixels you change
the amount of states you change
the difference between clearRect and width = width is that clearRect clears out the pixel data in the given area, while width = width clears out all pixel data and all states, like transformations, and styles. I think you already see the difference, there are a lot more things to consider (like Garbage Collection blocking your drawloop being one) but that would get a bit offtopic.
I'm not sure what you mean with overloading the browser. If you mean blocking and making the UI unresponsive then yes it's a thing that can and will happen since javascript is single-threaded and there are many ways you can accomplish it, but most likely not with such a reasonable operation :)
The worst thing you can do with drawing is make it super choppy on slow CPUs. Using requestAnimationFrame() instead of setTimeout()(which I assume you currently use because you mentioned 100ms) for your drawloop(s) is almost always a good and safe way to make sure your drawing will not block the UI.
the Canvas element tracks these [rectangles]
It doesn't track painting operations, as far as I know. (It's up to the implementation, but I don't know of any implementation that does.) You might be thinking of how it can save and restore things like transforms and paint colors.
I made a speedtest to compare Snap.svg (SVG) to FabricJS (CANVAS):
http://jsbin.com/tadec/7 function dummy().
In Chrome SVG renders in 120 ms, while CANVAS renders in 1100 ms. SVG is 9x faster than CANVAS.
Fabricjs.com page says in this example that Raphael takes 225 ms and Fabric takes 97 ms (parsing: 80, rendering: 17).
I have had an impression (after reading fabricjs.com and paperjs.org) that FabricJS and more generally Canvas is faster than SVG.
My speed test claims that SVG is significantly faster than Canvas (at least Snap.svg seems to be significantly faster than FabricJS).
Why FabricJS is so slow in my test? Have I made some mistake in comparison, because I'm surprised that SVG seems to be faster than Canvas in my speed test.
EDIT: My question is two-parted: Why rendering speed is so much slower in FabricJS and why dragging speed as well?
Your benchmark is broken in my opinion, because beside measuring drawing to canvas you are measuring parsing of a huge string containing a path, over and over again. Separate this code out of the loop and you should get more reliable results.
Measurements that are provided for canvas libraries are provided for drawing, not for parsing or other pre-processing work. If you use canvas like you use SVG, then yes, it will be slower. It is not intended to be used like SVG. FabricJS provides a way to do that, but it is not optimal. One solution would be to parse path once, and then use same path over and over instead of parsing it every time.
Also, measurements are given probably for drawing a canvas, not for interaction with parts. As you say in comments, rendering may be improved, but why does dragging a shape take so much longer? Because:
maybe path is being reparsed on each redraw (not sure how FabricJS works)
because SVG can redraw only certain parts of image that you are moving, and canvas is usually redrawn completely. Why? Because you can't "erase" part of canvas where a shape used to be. So entire canvas is erased, and new positions are redrawn.
Why do then people say canvas is faster than SVG for such scenarios? Because it is if you use it properly. It will be more work, but it will work much faster.
Don't use SVG paths for drawing shapes, or use simple paths and cache them
Cache shapes which you use often into off-screen (hidden canvas) and then copy from that canvas onto visible canvas when needed
If you have multiple independant layers of an image (for example 3 layers in a game, if you have background sky which is moving, background mountains which are moving slower and a character), use multiple canvases. Put canvases one over another, draw sky on the bottom canvas, draw mountains on second canvas and draw character on top canvas. That way, when character on top canvas moves, you don't have to redraw entire background.
I hope my answer is useful for you :)
so i have been trying to work on a canvas sketching app. and i had this problem where the line drawn are very unclean. As you can see from image below.
then i tried this and got the below result which is what i desire.
But this created a new problem, the eraser function wont work. Now I've been trying to make this two work for sometime, but just could'nt do it.
So what i want is a new smoothing technique or a way to make the above two work.
BTW here is the demo without smoothing: http://jsbin.com/axarun/1/edit
and here is the demo with smoothing: http://jsbin.com/aviluk/2/edit
Thnx in advance.
Method 1:
You can "force" the canvas to use anti-aliasing by doing this:
ctx.translate(0.5, 0.5);
Force in quotes as this is dependent on actual implementation in the browser you're using. Currently there is no way to enable/disable anti-alias by intention.
Method 2:
Set the canvas to "high-resolution" by setting the canvas itself to double size, but force it to be displayed in the original size by styling the canvas element with CSS rules:
<canvas id="myCanvas" width="1600" height="1200"
style="width:800px;height:600px" />
Just remember to double your mouse-coordinates as well as pen-width. This will eat 4x more memory and performance, but will give you high-resolution lines (in appearance).
In this case the browser will treat the canvas as an image and apply anti-aliasing as it does for any scaled image, and not the canvas' method for anti-alias.
Please note that this is not the same as applying scale transform to the canvas.
See demo of "hi-res canvas" here:
http://jsfiddle.net/q2t5A/
Method 3-ish:
For actual smooth lines you can check out my function to do so here:
http://www.codeproject.com/Tips/562175/Draw-Smooth-Lines-on-HTML5-Canvas
This takes an array of x/y couples and smooths the line (cardinal spline). The lines will go through the original points and no control points are needed.
This will of course imply that you record the actual points, and when mouse up you redraw the canvas using the curve() function in this code. That would be a correct approach in any case, to record the strokes in an array and then redraw (this will also allow you to use layers). There are workarounds to avoid render everything by using off-screen
canvases to store f.ex. each layer. By drawing a canvas onto another with a small non-integer offset will force anti-alias (but see point 1).
If I change the cursor of a page into a 'flashlight' (say, a circle), and I want to reveal an image only when passing the light (the circle) over it, what would be the best way to go about this? Using css clip? But then it can only do rectangles, so I'd have to use canvas? Perhaps there's an easy way to intersect the two images?
You can do this with a canvas easily.
Here's an example:
http://jsfiddle.net/gfZ5C/
On every mouse move, we clear the canvas, redraw the image, make a clipping region that is a circle cut out of a rectangle, and draw black on the entire canvas (which will draw only on the clipping region)
Make sense?
There are a lot of ways to achieve this effect and similar effects. You can also make much fancier light sources with a bit of canvas sorcery. See for instance my answer here: Canvas - Fill a rectangle in all areas that are fully transparent
I'm trying to draw a tiled background using Javascript on an HTML5 canvas, but it's not working because shapes that intersect the edges of the canvas don't wrap around to the other side. (Just to be clear: these are static shapes--no motion in time is involved.) How can I get objects interrupted by one side of the canvas to wrap around to the other side?
Basically I'm looking for the "wraparound" effect that many video games use--most famously Asteroids; I just want that effect for a static purpose here. This page seems to be an example that shows it is possible. Note how an asteroid, say, on the right edge of the screen (whether moving or not) continues over to the left edge. Or for that matter, an object in the corner is split between all four corners. Again, no motion is necessarily involved.
Anyone have any clues how I might be able to draw, say, a square or a line that wraps around the edges? Is there perhaps some sort of option for canvas or Javascript? My google searches using obvious keywords have come up empty.
Edit
To give a little more context, I'm basing my work off the example here: Canvas as Background Image. (Also linked from here: Use <canvas> as a CSS background.) Repeating the image is no problem. The problem is getting the truncated parts of shapes to wrap around to the other side.
I'm not sure how you have the tiles set-up, however, if they are all part of a single 'wrapper' slide which has it's own x,x at say 0,0, then you could actually just draw it twice, or generate a new slide as needed. Hopefully this code will better illustrate the concept.
// Here, the 'tilegroup' is the same size of the canvas
function renderbg() {
tiles.draw(tiles.posx, tiles.posy);
if(tiles.posx < 0)
tiles.draw(canvas.width + tiles.posx, tiles.posy);
if(tiles.posx > 0)
tiles.draw(-canvas.width + tiles.posx, tiles.posy);
}
So basically, the idea here is to draw the groupings of tiles twice. Once in it's actual position, and again to fill in the gap. You still need to calculate when the entire group leaves the canvas completely, and then reset it, but hopefully this leads you in the correct direction!
You could always create your tillable image in canvas, generate a toDataUrl(), and then assign that data url as a background to something and let CSS do the tiling.. just a thought.
Edit: If you're having trouble drawing a tillable image, you could create a 3*widthx3*width canvas, draw on it as regular (assuming you grab data from the center square of data as the final result), and then see if you can't draw from subsets of the canvas to itself. Looks like you'd have to use:
var myImageData = context.getImageData(left, top, width, height);
context.putImageData(myImageData, dx, dy);
(with appropriate measurements)
https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas/
Edit II: The idea was that you'd have a canvas big enough that has a center area of interest, and buffer areas around it big enough to account for any of the shapes you may draw, like so:
XXX
XCX
XXX
You could draw the shapes once to this big canvas and then just blindly draw each of the areas X around that center area to the center area (and then clear those areas out for the next drawing). So, if K is the number of shapes instead of 4*K draws, you have K + 8 draws (and then 8 clears). Obviously the practical applicability of this depends on the number of shapes and overlapping concerns, although I bet it could be tweaked. Depending upon the complexity of your shapes it may make sense to draw a shape 4 times as you originally thought, or to draw to some buffer or buffer area and then draw it's pixel data 4 times or something. I'll admit, this is some idea that just popped into my head so I might be missing something.
Edit III: And really, you could be smart about it. If you know how a set of objects are going to overlap, you should only have to draw from the buffer once. Say you got a bunch of shapes in a row that only draw to the north overlapping region. All you should need to do is draw those shapes, and then draw the north overlapping region to the south side. The hairy regions would be the corners, but I don't think they really get hairy unless the shapes are large.... sigh.. at this point I probably need to quiet down and see if there's any existing implementations of what I speak out there because I'm not sure my writing off-the-cuff is helping anybody.