I've looked around for layering objects within the same canvas but haven't found a lot of information about it.
At the moment I've used the multiple canvas technique to layer things on top of each other
example:
canvas Holder <--- this holds all other canvas's
loading canvas
menu canvas
game canvas
background canvas
and by adding them to the "stage = new stage (mainCanvas)" in a specific order, i get the desired layering
stage.addChild(background);
stage.addChild(game);
stage.addChild(menu);
stage.addChild(loading);
This works great, however I'm wondering whether there is a way to change the zIndex of an image added to the 'game' canvas if I had 2 images in that canvas?
I've seen this sort of thing done in the fieldrunners game, the game follows a grid like format and when you place a shooter in the square above another shooter, is gets repositioned behind it..
http://fieldrunnershtml5.appspot.com/#sd --- works in chrome
any ideas how it was done?
Thanks
There is no need for multiple canvases. When you work with a game in canvas 2d you usually clear and redraw the canvas ~60 times per second. What you draw last ends up on top. So in order to simulate layers you sort all game objects in an array based on their z-index then you iterate over all objects in the array, invoking their draw methods.
There is much room for optimizing such a renderer, but this is a basic and simple way to make it work.
A canvas is just pixels - it has no "layers".
If you want to perform parallax scrolling, that sort of thing, put multiple canvases in the same place, and use transparency to show the ones behind.
Your technique using multiple canvases to implement layers is totally good approach. You should also keep track on which layer needs to be cleared/redrawn - for example map should be refreshed only when scrolled or GUI/HUD really doesn't need to be redrawn 60 times per second.
There is no such thing as z-index or objects in canvas, all mechanisms depends on your own implementation. For example you can make an array of commands or objects to draw - then sort it by zIndex (or whatever you name it) and execute each element.
Related
I'm trying to create a small website using the FabricJS library, which adds additional features to the web canvas element.
My issue that I, however, have is that i want to resize the canvas (in red) so that it fills the whole webpage.
On this canvas, there is a background image (in green) where I'll create some drawings on (in orange, this could be lines, squares,...).
Now, I would like to export all drawings in a coordinate system relative to the image and not to the whole canvas, because it should be possible to freely move around and zoom in/out the image for an enhanced drawing experience.
My idea, on how to solve this, would be to calculate the image position relative to the canvas and subtract them from the drawings - but that includes a lot of calculation.. Maybe there is a more genius approach with FabricJS?
Moreover, how can i guarantee that my drawings move around and zoom in/out with the image, so that my drawings are always true to the image?
I've thought about this for days and came to the realization that i need input from the professionals.
I think toLocalPoint() might help. Given an object imageObj and absolute coordinates left and top of your drawing, you can find the relative coordinates like this:
const abs = new fabric.Point(left, top)
const rel = imageObj.toLocalPoint(point, 'left', 'top')
console.log(rel.x, rel.y)
As for your second question: there is no easy way to "tie" two objects together, other than grouping them - and I assume you don't want to group them. Therefore, you would need to listen to all the appropriate events emitted by one object and make the adjustments to the other object in their handlers. To find out what events make sense to listen to in your case, see the events demo.
i'm a beginner in Javascript so please bear with me.
Basically I'm making a sandbox drawing facility for a website using Javascript. And this is done using the canvas. What I need to do is to be able to resize the canvas dynamically but at the same time keep everything on the canvas to scale.
I don't think this question has been asked before. It seems trivial but I currently have all my objects on the canvas defined in absolute coordinates. I also have mouse events to use to draw things. And when I want to enlarge the canvas (by doubling the size say). All the objects inside won't be enlarged properly to scale and the mouse coordinate system would be messed up too.
Only solution i can think of is add a scale factor to ALL my drawing parts, but this is very tricky with a lot of code. Is there a better way?
If you don't mind jaggies on your double-sized canvas drawings then you can simply use CSS to double-size your canvas. Then divide every incoming mouseEvent coordinate by 2.
If you don't want jaggies on your double-sized canvas then:
Double-size the canvas element: canvas.width*=2 and canvas.height*=2 This automatically erases all canvas content.
Scale up the canvas: context.scale(2,2)
Redraw all your drawing parts using the unchanged original coordinates. A happy note: you do not have to scale any of your drawing coordinates -- context.scale automatically does that for you.
Divide every incoming mouseEvent coordinate by 2.
I am creating a small isometric online game and would like to use the sheetengine.
Here is my basic idea:
create a scene of the size approx 4100x3000
creating 75x45 basesheets (since the World is 75x45 fixed and wont increase)
To place some objects on the map I use the class Sheet (I will only change the size and the position of the sheets after placing them)
For Zoom In/Out I actually redraw the whole scene - so I destroy all the basesheets and then place them from new in a bigger size - as for the Sheets ... I'm trying to change their size and their position
For moving the map I'm using the method scene.setCenter - since the scene is already of that huge size, I don't have to redraw the map after the translation
My questions:
How can I change the position and the size of the sheet after placing them? I know that using the SheetObject you can do all that, but I will have a huge amount of objects placed and the calculation time is taking to long with SheetObjects. That's why I'm not using that class... An object of the "class" Sheet has a property
sheet.data.translationx
I coudl also manipulate this and thsi works, but there must be method and I couldn't find it
Is there a better way of handling big scenes with sheetengine? I mean creating a scene that big is definitely not a good Idea. But if I make it smaller, I will not see the whole world after a translation - so how could I handle it differently?
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'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.