I am now entering Kinetic and it has made it far easier for me to draw on canvas. However, building a game app, I need to clear the rectangle on every animation request. They're controlled by an fps cap script, but still, there are about 50 updates per second.
Kinetic's .removeChildren() method not only clears the canvas, it deletes the canvas node from the DOM. Doing so not only makes DOM queries inconsistent by intervals of .02 second, but also drops my FPS rate by about 60% in comparison to stock HTML5 canvas handling on every machine I ran the game on.
Is there a KineticJS method for clearing the canvas in a manner such as clearRect()'s?
Edit:
I have also made sure it's not a problem on any other part of the program. Call stack doesn't overflow, the FPS drop is just due to DOM changing twice every .02 second.
Edit 2:
I have tried the following:
Ignore the layer before and create a blank rectangle to fill up the visible part of the canvas. It dropped my frame rate to about 14 FPS;
Use the .clear() method. It solved the DOM consistency problem but the frame rate got even lower than before.
It seems the only solution would be calling the default HTML5 clearRect() method, but that would mean creating the canvas element by hand (and possibly making Kinetic useless as a library for my app).
Edit 3:
As for the app, I've started using standard HTML5 canvas since I have a deadline. I'd still like to see a Kinetic solution though - it might be helpful in the future. It surprises me to see such a simple thing is so hard, if not impossible, in a popular library like KineticJS.
You can use layer.clear with a bounding area to clear just the "dirty" part of your layer.
// tell layer not to auto-clear on layer.draw
layer.setClearBeforeDraw(false);
// clear the "dirty" portion of the canvas
layer.clear(100, 100, 150, 150);
// adjust some animation values and
// just draw the element that has changed
myRect.draw();
You should try to create new Layer for example:
var newLayer = new Kinetic.Layer();
Or call this function:
Canvas.clear();
Kinetic makes it very easy to draw using layers, groups and shapes.
If your view is properly make of these items you can easily remove them and they will be removed from the stage.
Perhaps you need to rewrite you code to make it work better in kinetic.
if you think your code is properly written you can try (as a workaround) to create kinetic rectangle and fill it with whatever you want to simulate a clear.
Related
Background
I'm making a simple online drawing app so that I can practice my JS and canvas skills. It's going really well, with the ability to draw freely, and also the ability to draw straight lines.
How my drawing app works
It registers mouse and touch events, which handle all of the drawing and saving.
Here's some example code:
var tools={
"pencil":{
"started": false,
... (some data used by the pencil tool),
"start":function(e){
if(currentTool!=="pencil")return;
// Make sure that the selected tool on the toolbar is the pencil
// code for mousedown/touchstart
tools.pencil.started=true;
},
"move":function(e){
if(!tools.pencil.started)return;
// code for mousemove/touchmove
},
"end":function(e){
if(!tools.pencil.started)return;
// code for mouseup/touchend
tools.pencil.started=false;
}
},
"other tools":{
...
}
};
// And here would be a function which adds the mouse and touch events
Here is my pencil tool:
var tools={
pencil:{
started:false,
start:function(e){
if(currentTool!=="pencil")return; // Make sure the pencil tool is selected
e.preventDefault();
e=e.clientX?e:e.touches[0]; // Allow touch
context.beginPath(); // Begin drawing
context.moveTo(e.clientX-50,e.clientY); // Set the position of the pencil
tools.pencil.started=true; // Mark the pencil tool as started
},
move:function(e){
if(tools.pencil.started){ // Make sure the pencil is started
e.preventDefault();
e=e.clientX?e:e.touches[0];
context.lineTo(e.clientX-50,e.clientY); // Draw a line
context.stroke(); // Make the line visible
}
},
end:function(e){
if(tools.pencil.started){
e.preventDefault();
//tools.pencil.move(e); // Finish drawing the line
tools.pencil.started=false; // Mark the pencil tool as not started
}
}
}
};
Ignore the -50 parts (they're just to adjust with the sidebar). This works, but doesn't save to localStorage.
Problem
TL;DR: I need to save everything on the canvas into some storage (I was currently using localStorage but anything would work, although I would prefer usage of the client-side only). I can't figure out how to efficiently store it, where 'efficiently' means both fast and accurate (accurate as in it stores the whole line). Lines can be stored, but hand-drawn items I haven't figured out yet.
Explanation:
When the user resizes the window, the canvas resizes to the window size (I made this happen, not a bug). I was able to make it that when you resize, it first saves the drawings onto a temporary canvas and then, after the main canvas resizes, redraws them back. But there's a problem. Here's an example to make it clear:
You open the draw app and fill the screen with drawings.
You open DevTools and some of the drawings get covered
When you close the DevTools, then those drawings are gone.
The reason is because since the canvas got smaller, the drawings that went off of it were lost, and when it came back to the original size, they weren't visible (because they're gone). I decided to save everything to localStorage so that I can retain them (and also for some more features that I may add). The lines are working (since they only need to say, "I am a line, I start at x,y and end at x,y". That's it for a line of any size. But for hand-drawn images, they can go anywhere, so they need to say, "I am a pixel, I am at x,y", but many times for even remotely complex images.
Attempted solution
Whenever they move the mouse, it saves to a variable which then updates to localStorage. The problems with this is that if you go fast, then the lines isn't complete (has holes in it) and localStorage is storing a lot of text.
Question
How can I efficiently store all of the user's hand-drawn (pencil tool) images (client-side operations preferred)?
My suggestion is to consider the target use of this app and work backwards from it.
If you're doing something like a MS Paint drawing app, storing a canvas as a bitmap may be better. To deal with cropping issues, I would suggest that you use a predefined canvas size or a setup stage (with a maximum size to prevent issues of saving to LocalStorage).
If you're worried about image size being restrictive due to memory cap, you can also utilize compression.
ie.
canvas.toDataURL('image/jpeg', 0.5);
👆 will compress the image and return a string that will be localStorage friendly
If, on the other âś‹, you are building something more like an excalidraw app, where you want to maintain the ability to edit any drawn object, then you'll need to store each object individually. Whether you use canvas or SVG doesn't really matter imho, but the SVG can make it a bit easier in the sense that the definition of how to store each element is already established, so you don't need to re-invent the wheel, so-to-speak, just need to implement it. Use of SVG doesn't necessarily preclude you from using canvas, as there are a couple SVG-in-canvas implementation, but if you're doing this as a learning project (especially for canvas), it's likely not the route you want to take.
I'm wondering how to make a viewport that follows the player such as in sidescrolling games. I have a semi-working version, but it requires me to move everything except the player.
ctx.translate(canvX,canvY);
drawBlocks();
ctx.restore()
This works for now, but I will have to draw enemies and other objects, and I don't want to constantly have to redo the process. I'm looking for a simple solution that basically involves a camera that follows the player. Is this possible?
Use something like three.js for games. Because you have to draw as many frames per second, and canvas just isn't great for that (if you don't believe me now, wait until you have to draw more things on the screen).
However, for your current code, one thing I notice is you're missing a save.
If that's not the problem, which I dont think it is, based on your question, you don't want to re-draw everything, only the background? You could actually use multiple layers, so that each enemy is an HTML element, and you only redraw the enemy when their animation frame changes. Then you just move their element ( a little cheaper than re-drawing in terms of performance ).
THREE.JS is what you should learn.. it will really help you out.
I'm using fabric.js for a collaborative whiteboard project. I want freehand drawing paths to be drawn concurrently by several users. To do this I figured I could create a new PencilBrush for each remote user and simulate freehand drawing by calling the onMouseDown(), onMouseMove() and onMouseUp() methods of the PencilBrush class whenever the remote user performs those actions. I've created a jsfiddle that does something like that: http://jsfiddle.net/dkostro/7sx8N/21/
It draws two paths concurrently and the user can also draw on the canvas meanwhile. As you see one of the paths is never seen before onMouseUp() and freehand drawing it's not very pretty. I figured the problem was that each PencilBrush instance clears the upper canvas element before calling the _render function, thus only one of the paths can appear simultaneously.
Can you think of a way to make it work smoothly without having to mess up too much with fabric.js code? The one solution I thought of is having a separate canvas element for each PencilBrush, but that would be messing up with fabric.js code, and may not be a clean way to do it. Any other suggestions?
EDIT
I have implemented this one solution I have been thinking of in my question (separate canvas element for each user). http://jsfiddle.net/stropitek/DzygL/14/. I am not sure to what number of users this can scale to, or if this is a good solution, but in this example at least it seems to work well.
I’m trying to build a game in canvas just to improve my skills set. The idea is there’s two objects on the screen that are player-controlled. I initialise the game with players, and then start a game loop that listens for player input.
At the moment, I can draw the players on the canvas (simply rectangles at this stage). However, I’m having trouble moving these objects on each “tick” of the game loop. At the moment, the rectangle is just drawn on top of the rectangle in the previous frame; I want to clear the canvas and re-draw the “players” in each tick.
How would I go about this? And is it the best way, or is there a better approach?
I’d post a code sample, but my JavaScript file is quite verbose and I’m hoping the description above is sufficient.
If the appearance of the players doesn't change from frame to frame, then I would use three separate canvas elements: one for the background and one for each player. Then you don't have to redraw anything, just change the CSS left and top values for each player canvas.
If the appearance of the players does change, then you'll need to redraw them. But you still might want to use a separate canvas for each one. That way you don't have to redraw the background. You can just draw and position each player.
Of course if the background changes or moves, you'll need to redraw it anyway. In that case you might just use a single canvas, or experiment with the separate canvases. With regard to the specific question of how to clear the background (or any canvas) before redrawing, here are a couple of ways to clear a canvas.
Either way, use requestAnimationFrame() in browsers that support it, instead of setTimeout() or setInterval(). If you search for requestAnimationFrame polyfill you will find many examples of how to do this and still support old browsers. Here's a good requestAnimationFrame polyfill.
Here is a good tutorial with a basic game loop. It renders a background, monsters and a player.
http://www.lostdecadegames.com/how-to-make-a-simple-html5-canvas-game/
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
render();
then = now;
};
You update your object positions in update and then call render.
I'm sorry, but having a separate canvas for each element in the scene, as another user sugested, is a terrible idea. And that won't improve your skills set in anyway.
Yes, you have to redraw everything on a single canvas, even the objects that don't transform from one frame to another. Clearing the canvas is simple, use the context.clearRect(0, 0, canvas.width, canvas.height) or do a fillRect to paint the background with some color, or draw an image on the entire canvas to have a static background, anything that covers the whole canvas will do.
So, you update the objects, clear the canvas, then, redraw everything. Since you just have 2 rects and the only thing that changes is their position this will be really easy to implement.
When you start making more complex things you might want to go for a more object-oriented aproach, by that I mean each object in your scene needs to "know how to draw themselves", like:
var player1 = new PlayerRect(position);
player1.draw(context);
This way you'd update it's position in the loop doing something like this:
player.position.x += 10;
In case you end up with hundreds of objects, all you have to do is a list where you could add/remove these objects and a loop calling their draw method.
I mean, that's the way I do it.
I want to implement a drawing pane (similar but smaller version to what visio gives for flow charts) in mozilla canvas.
Is there any support for this?
I have used jQuery till now to create the rectangles and move them around. While this is easy here..creating lines (connections between objects) is a real pain. I am using some crude way to color pixel by pixel in javascript and it is neither looking good nor scalable and also I need to build a lot of functions to make the connections stick to a set of objects etc.
Does anyone know if the canvas and the functions available there will make my life easier.
Any pointers to what is a better solution in this case. (I am hoping it is not applet)
Thanks in advance.
Yes you can use canvas for that. Drawing simple shapes and scaling them is pretty simple.
But if you need to edit the shapes after you have drawn them, you will have to invest some more work. Canvas draws in a so called "immediate mode", which means, that it does not know what you have painted right after you have painted it. It does not keep track of painted shapes. If you need that you will have to implement that on your own.
I have done this using the isPointInPath() function which can be used to test if a user clicks on a particular point. I keep track of my painted objects using the MVC-Pattern (Model-View-Controller), so that I can find out which Shape has been clicked on.
Another alternative might be fabrics.js which should be very close to what you need.
Please follow this link :
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial
LMK if it helps!
Following steps may help:
1. Create and add a canvas to the DOM :
var myCanvas = document.createElement('canvas'); document.body.appendChild(myCanvas);
2. Set the width-height of canvas :
myCanvas.width=200;
myCanvas.height=200;
3. Get the context of the canvas and start drawing on it :
var gc = myCanvas.getContext('2d');
4. Code to draw rectangle :
gc.strokeRect(50,50,50,50);
5. After this, add mousehandlers(mousedown,mousemove,mouseup)/touchhandlers(touchdown,touchmove,touchup) on the canvas and handle the movement accordingly.
Either of these jQuery plugins are great for drawing panes:
jCanvas For drawing any simple and even complex shapes
sketch.js for drawing in general.
They are both responsive and compatible.