I wrote a little drawing script (canvas) for this website: http://scri.ch/
When you click on the document, every mousemove event basically executes the following:
- Get coordinates.
- context.lineTo() between this point and the previous one
- context.stroke() the line
As you can see, if you move the cursor very fast, the event isn’t triggering enough (depending on your CPU / Browser / etc.), and a straight line is traced.
In pseudocode:
window.addEventListener('mousemove', function(e){
myContext.lineTo(e.pageX, e.pageY);
myContext.stroke();
}, false);
This is a known problem, and the solution is fine, but I would like to optimize that.
So instead of stroke() each time a mousemove event is triggered, I put the new coordinates inside an array queue, and regularly draw / empty it with a timer.
In pseudocode:
var coordsQueue = [];
window.addEventListener('mousemove', function(e){
coordsQueue.push([e.pageX, e.pageY]);
}, false);
function drawLoop(){
window.setTimeout(function(){
var coords;
while (coords = coordsQueue.shift()) {
myContext.lineTo(coords[0], coords[1]);
}
myContext.stroke();
drawLoop();
}, 1000); // For testing purposes
}
But it did not improve the line. So I tried to only draw a point on mousemove. Same result: too much space between the points.
It made me realize that the first code block is efficient enough, it is just the mousemove event that is triggering too slowly.
So, after having myself spent some time to implement a useless optimization, it’s your turn: is there a way to optimize the mousemove triggering speed in DOM scripting?
Is it possible to “request” the mouse position at any time?
Thanks for your advices!
If you want to increase the reporting frequency, I'm afraid you're out of luck. Mice only report their position to the operating system n times per second, and I think n is usually less than 100. (If anyone can confirm this with actual specs, feel free to add them!)
So in order to get a smooth line, you'll have to come up with some sort of interpolation scheme. There's a whole lot of literature on the topic; I recommend monotone cubic interpolation because it's local, simple to implement, and very stable (no overshoot).
Then, once you've computed the spline, you can approximate it with line segments short enough so that it looks smooth, or you can go all-out and write your own Bresenham algorithm to draw it.
If all this is worth it for a simple drawing application... that's for you to decide, of course.
Cool site, unfortunately there is no way to request the current position of the mouse with JavaScript, the only hooks you have are the events you're already using. If you must have more control I'd look at using flash where you can change the frame rate and request the mouse position.
trace("Mouse X: " + _xmouse);
trace("Mouse Y: " + _ymouse);
Related
I am using Phaser.js and its p2 physics in order to simulate what "fluid bodies". You can see in this example that a kind of fluid body is created (credits to John Watson). The only possible interaction is with mouse movement.
I have noticed some interesting properties that could probably help me getting what I pretend which are restitution, gravity and damping. All of these are in included in the shown example.
// Add a force that slows down the droplet over time
droplet.body.damping = 0.3;
// Add bounciness and gravity
this.game.physics.p2.restitution = 0.8;
this.game.physics.p2.gravity.y = 250;
After some research and reflexion I concluded that the body I seek must be more united than the example body which means that, within certain limits, a force with the direction of the center of the body (center +- top of body) should be applied to all the "small bodies" that make the body. I suppose that after achieving that even if I move the entire body with cursors it will move all together. The overall efect I want is a pile-type body form (less mass on upper part and more mass on the bottom part):
The only way for the body to lose mass should be an external force applied to the body that surpasses the resistance force that keeps the body united.
Even after researcing for quite a while I seem very lost in the matter...
Should I literally opt for p2 forces ( I believe that exists ) ?
Should I use springs to connect all the small bodies? (springs)
How would I always get the "center of the body"?
Thanks.
Disclaimer: I have not used Phaser.js, so I can't help you with the framework. I will however try to share some of my ideas on the problem, and hopefully it helps you.
I recently wrote this answer regarding plasticity (which could be of interest to you), but what you want is slightly different.
The simulation
First, lets talk some about the simulation you showed. You write "fluid bodies", but from what I see in the code example is nothing fluid -- it's a pure particle simulation with "cheated" physics features that comes from (1) the automatic damping of the particles and (2) the restitution which in normal speak means how elastic a collision with the object is (e.g. the 0.8 value in this case means that 20% of the kinetic energy is lost with every collision). So basically, we have a particle simulation with a lot of damping.
That this yields something that looks 'fluid like' is really pretty cool, but my guess is that this is also due to the rendering (e.g. show the particles as small disks instead of the blurred thing and it will look a lot more like what you'd expect).
Your questions
Should I literally opt for p2 forces ( I believe that exists ) ?
I am actually not sure what this means, but I'm guessing it's Phaser related.
Should I use springs to connect all the small bodies? (springs)
No. If you use a elastic potential to calculate the force you will get an elastic body as differing from plasticity which is what you're looking for. What other function you use will entirely determine the behaviour of your simulation, so going with this idea there will be a lot of experimentation.
If x_cm is the position (vector) to the center of mass, and x[i] is the position of particle i, then one example could be:
F(i) = F_constant*(x_cm - x[i])
A purely linear function. F_constant is some (constant) coefficient. Particles far away will receive a greater force than particles nearby. You would then calculate this force for all particles, and apply it accordingly.
How would I always get the "center of the body"?
The center of mass of the body is straight forward to calculate. In pseudo code it looks like this:
var x_cm
var total_mass = 0
for each particle p:
total_mass += p.mass()
x_cm += p.mass()*p.position()
x_cm /= total_mass
Formulas are hard to display nicely here, but it's just the same as described on wikipedia.
Another possibility
This answer is already long, but just a finishing thought. From my perspective it sounds like you want something similar to sand (i.e. it is a particle simulation similar to the one you showed, but it piles as well). A common way to simulate sand is by the exact simulation as you have above, but with friction added to the particles. If this is possible to do with Phaser I don't know, but I would expect it being easy to do.
EDIT: Had a typo in the last sentence. Simulating sand with Phaser, by adding friction to the example simulation, should be easy.
I've created a function that adds a graphic to the canvas stage and animates it via a tween, waits a few milliseconds then spawns another
There are a few things I could do with some help with,
firstly I need to slow down the assets to a stop after a few seconds of them playing at normal speed (I'm animating lines in the middle of a road)
secondly when the animation starts there's nothing on the screen,because they are road marking something has to display at start
and any ideas on how I remove each item when animation finished
Here's what I have so far
//this is called from the tick handler
function lines(){
duration = 1000;
spawnCounter--;
console.log(spawnCounter)
if(spawnCounter == 0){
spawnCounter = sNum//20
bolt = new lib.Bolt();
bolt.x=280
bolt.y = 120;
bolt.rotation = -66;
stage.addChild(bolt);
createjs.Tween.get(bolt).to({x:0,y:0, scaleY:.6, scaleX:.6},duration)
}
}
Removing items at the end of the tween is fairly simple:
createjs.Tween.get(bolt)
.to({x:0,y:0, scaleY:.6, scaleX:.6}, duration)
.call(function(event) {
stage.removeChild(bolt);
});
Slowing down the entire animation the way you have made it might be tricky, since you would probably want the assets to slow down at the same rate, so you can not just change the duration of the tween. You might want to consider NOT using a tween for the animation, and instead just controlling the "visible" items in the tick, and removing them when they get too far.
I created a quick sample showing how this could work.
http://jsfiddle.net/wj15awj4/
Generate the items after a certain "distance" has elapsed
Remove items past the edge
Scale and alpha the items based on the position. The scale is kind of odd, since the road has no perspective. This would be better solved transforming the canvas using CSS or WebGL.
When the road is clicked, the "speed" is tweened down (or back up), and since the items are created based on how much "distance" has passed, they will continue to be created at roughly the same rate, despite moving slower over time.
Hope this helps!
After a sleepness night I discovered something about this question which I think is fundamentally mind boggling, at least to me.
Mouse coordinates ARE NOT PRECISE (I guess at a high speed of processing where the whole canvas has to be recreated when movement occurs) as in my codes above. I have tested this code piece by piece and discovered that the problem is not in my loop, but in the precision of
if ((newMouseX !== mouseX) && (newMouseY !== mouseY)).
If you tested this part of code by slower times (which will allow your eyes to detect the difference in coordinates when 'it stops', then you will realise that newMouseX & mouseX are off by 1-2 pixel 90% of the time, > 2 pixel 9% of the time, and only equal about 1% of the time. (I did not measure the statistics but that is what I picked on several rounds of testing).
I can't get it to work in fiddle but I think you can copy it to your testing ground to see what I mean. If you can get it to work in fiddle it would be great so experts can give it a short :)
This means that the mouse is considered to be 'moving' by my code even when it should have 'stopped', and thus 'stops' several times in between, therefore calling the loop too many times in a second, which is the problem I have been having.
I would be happy to hear comments from other experts, including those who can test this and come up with a statistical precision/advice.
My advice, and solution for the moment, is to consider movement when the difference is more than 10 pixels of either coordinates. Of course this presents a problem, but I can leave with that until some better solution comes up.
so instead of
if ((newMouseX !== mouseX) && (newMouseY !== mouseY))
i have used
if (( Math.abs(newMouseX - mouseX) > 10) || ( Math.abs(newMouseY != mouseY) > 10) )
Another thing to consider is how to deal with the mouse position when it goes off my target canvas area... that looks like an infinite movement at the moment!
The Question:
How can I get the precise mouse coordinates so I can compare mouseX & newMouseX?
Thanks.
Mouse precision is determined by the hardware. A high-precision mouse will produce different results than a built-in mouse pad for instance (not to mention touch devices).
However, this is not the problem with your code and your scenario. You are only listening to the mousemove event. It will by definition only throw an event when you move the mouse - hence the new mouse position can never be at the same position as the previous one. That would be impossible and should be off by 100% (unless you are triggering two+ moves where the last goes back to the fist position before you check).
Normally one would listen to the mousedown and mouseup events as well as they are not dependent on a mouse move to trigger. Detecting start and stop solely based on mouse movement is considered impossible under all possible circumstances.
You can do a compromise and make a definition of what a start is and what a stop is, ie. if the mouse has not moved after x milliseconds it is considered a stop (start would be on first move).
This means you will need to follow this rule every time you need to detect a stop. Imaging doing a drawing and sometimes you draw sometime slow other times fast. Or, how do you move the mouse to a new position without drawing anything... There is a good reason for the mouse button(s) to be invented :-)
The rule will soon prove to be useless (or overly complicated prone to more than one error).
As to mouse positions outside canvas there are several ways to handle this.
You can get the canvas bounds by calling:
var canvasRect = canvas.getBoundingClientRect();
which gives you properties to check when mouse position is inside or outside this rectangle.
Another way is to listen to the mouseleave and mouseenter events on the canvas element.
A third is to actually use the mouse buttons. When mouse button is held down on the canvas element you set a flag so mousemove events are considered.
This will keep listening until the mouse button is released. If you release it outside canvas and is using the canvas mouseup event it won't be detected. Therefor you should listen to the window's mouseup event which will trigger in either case.
This also goes for mousemove events. Using the window event will allow you to record positions outside canvas. If you don't want to do this you can use canvas' mousemove which will clip at the canvas' boundaries.
It boils down to:
Use the mousedown, mousemove and mouseup events in combination and you'll be fine. All these events delivers clientX and clientY for mouse positions.
And if I may - you can also test by going to my easyCanvas project and run the sample:
Sample - mouse event details
This will show you details for mouse down, move and up (the details are extended with other information, but you can at least verify mouse positions - do a click without moving and you see the mouse position is exactly the same).
http://jsfiddle.net/6czap/74/
I hope i helped with this :)
$("div").mousemove(function(e){
var pageCoords = "( " + e.pageX + ", " + e.pageY + " )";
var clientCoords = "( " + e.clientX + ", " + e.clientY + " )";
$("span:first").text("( e.pageX, e.pageY ) : " + pageCoords);
$("span:last").text("( e.clientX, e.clientY ) : " + clientCoords);
});
I'm writing a game using HTML5/WinJS on Windows 8. I'm trying to produce the effect of a bullet or missile firing at something; however, I can't seem to get the object to go through another image in the background without trailing a border. My working theory is that the border I'm seeing is caused by using clearRect. Here's my code:
var moveBullet = function(missile) {
if (missile.Image == undefined || missile.Image == null) {
var image = new Image();
image.src = "/images/missileImg.png";
image.onload = function () {
canvasContext.clearRect(missile.PointX - (image.width / 2), missile.PointY, image.width, image.height);
missile.PointY += BULLET_SPEED;
canvasContext.drawImage(image, missile.PointX - (image.width / 2), missile.PointY);
};
} else {
canvasContext.clearRect(missile.PointX - (missile.Image.width / 2), missile.PointY, missile.Image.width, missile.Image.height);
missile.PointY += BULLET_SPEED;
canvasContext.drawImage(missile.Image, missile.PointX - (missile.Image.width / 2), missile.PointY);
}
}
Is there a way to achieve this effect without using clearRect, or a more efficient way of restoring the background as it moves past?
Make your clearRect area a few pixels larger than the missile image. Drawing on a canvas in general has some built-in anti-aliasing. This means that if you draw a line with one color, then draw the same line with the background color, you'll not remove the original line. Something similar might be happening here, in which case a few extra pixels should help.
That said, there's a caveat to be aware of here.
First, I assume the background is separate element from the canvas? It looks like it as you're not redrawing that part on the canvas itself.
The reason I ask is that making repeated calls to clearRect on the same canvas will eventually show performance problems. What happens is that every call to clearRect accumulates into a complex region within the canvas--essentially its transparency mask. So every time the canvas has to be rendered, which happens any time you change it, it has to process that transparent area. Gradually, as you leave more and more small clearRect trails across the canvas, this region will become more and more complex and performance will drop.
I did this experiment with the Blizzard demo on the IE Test Drive site once, where I wondered why the demo was clearing the entire canvas with every animation frame. So I tried just clearing the trail behind each snowflake (and made each one a little bigger as I suggest above, because I had trails). Seemed like the right thing to do, but the performance plummeted by several orders of magnitude. Asking around within the IE team, they confirmed the region behavior I describe.
So the best thing to do, actually, is to do a clearRect on the entire canvas with every frame, then redraw the missile and any other bits that you're animating. This may seem counter intuitive, but ends up working best and avoids all these glitches with pixel trails.
I've built an analytical data visualization engine for Canvas and have been requested to add tooltip-like hover over data elements to display detailed metrics for the data point under the cursor.
For simple bar & Gaant charts, tree graphs and node maps with simple square areas or specific points of interest, I was able to implement this by overlaying absolutely-positioned DIVs with :hover attributes, but there are some more complicated visualizations such as pie charts and a traffic flow rendering which has hundreds of separate areas defined by bezeir curves.
Is is possible to somehow attach an overlay, or trigger an event when the user mouses over a specific closed path?
Each area for which hover needs to be specified is defined as follows:
context.beginPath();
context.moveTo(segmentRight, prevTop);
context.bezierCurveTo(segmentRight, prevTop, segmentLeft, thisTop, segmentLeft, thisTop);
context.lineTo(segmentLeft, thisBottom);
context.bezierCurveTo(segmentLeft, thisBottom, segmentRight, prevBottom, segmentRight, prevBottom);
/*
* ...define additional segments...
*/
// <dream> Ideally I would like to attach to events on each path:
context.setMouseover(function(){/*Show hover content*/});
// </dream>
context.closePath();
Binding to an object like this is almost trivial to implement in Flash or Silverlight, since but the current Canvas implementation has the advantage of directly using our existing Javascript API and integrating with other Ajax elements, we are hoping to avoid putting Flash into the mix.
Any ideas?
You could handle the mousemove event and get the x,y coordinates from the event. Then you'll probably have to iterate over all your paths to test if the point is over the path. I had a similar problem that might have some code you could use.
Looping over things in this way can be slow, especially on IE. One way you could potentially speed it up - and this is a hack, but it would be quite effective - would be to change the color that each path is drawn with so that it is not noticeable by humans but so that each path is drawn in a different color. Have a table to look up colors to paths and just look up the color of the pixel under the mouse.
Shadow Canvas
The best method I have seen elsewhere for mouseover detection is to repeat the part of your drawing that you want to detect onto a hidden, cleared canvas. Then store the ImageData object. You can then check the ImageData array for the pixel of interest and return true if the alpha value is greater than 0.
// slow part
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillRect(100,100,canvas.width-100,canvas.height-100);
var pixels = ctx.getImageData(0,0,canvas.width,canvas.height).data;
// fast part
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (pixels[idx]) { // alpha > 0
...
}
Advantages
You can detect anything you want since you're just repeating the context methods. This works with PNG alpha, crazy compound shapes, text, etc.
If your image is fairly static, then you only need to do this one time per area of interest.
The "mask" is slow, but looking up the pixel is dirt cheap. So the "fast part" is great for mouseover detection.
Disadvantages
This is a memory hog. Each mask is W*H*4 values. If you have a small canvas area or few areas to mask, it's not that bad. Use chrome's task manager to monitor memory usage.
There is currently a known issue with getImageData in Chrome and Firefox. The results are not garbage collected right away if you nullify the variable, so if you do this too frequently, you will see memory rise rapidly. It does eventually get garbage collected and it shouldn't crash the browser, but it can be taxing on machines with small amounts of RAM.
A Hack to Save Memory
Rather than storing the whole ImageData array, we can just remember which pixels have alpha values. It saves a great deal of memory, but adds a loop to the mask process.
var mask = {};
var len = pixels.length;
for (var i=3;i<len;i+=4) if ( pixels[i] ) mask[i] = 1;
// this works the same way as the other method
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (mask[idx]) {
...
}
This could be done using the method ctx.isPointInPath, but it is not implemented in ExCanvas for IE.
But another solution would be to use HTML maps, like I did for this little library : http://phenxdesign.net/projects/phenx-web/graphics/example.htm you can get inspiration from it, but it is still a little buggy.
I needed to do detect mouse clicks for a grid of squares (like cells of an excel spreadsheet). To speed it up, I divided the grid into regions recursively halving until a small number of cells remained, for example for a 100x100 grid, the first 4 regions could be the 50x50 grids comprising the four quadrants.
Then these could be divided into another 4 each (hence giving 16 regions of 25x25 each).
This requires a small number of comparisons and finally the 25x25 grid could be tested for each cell (625 comparisons in this example).
There is a book by Eric Rowell named "HTML5 CANVAS COOKBOOK". In that book there is a chapter named "Interacting with the Canvas: Attaching Event Listeners to Shapes and Regions". mousedown, mouseup, mouseover, mouseout, mousemove, touchstart, touchend and touchmove events can be implemented. I highly suggest you read that.
This can't be done (well, at least not that easily), because objects you draw on the canvas (paths) are not represented as the same objects in the canvas. What I mean is that it is just a simple 2D context and once you drawn something on it, it completely forgets how it was drawn. It is just a set of pixels for it.
In order to watch mouseover and the likes for it, you need some kind of vector graphics canvas, that is SVG or implement your own on top of existing (which is what Sam Hasler suggested)
I would suggest overlaying an image map with proper coordinates set on the areas to match your canvas-drawn items. This way, you get tooltips AND a whole lot of other DOM/Browser functionality for free.