Canvas Animation - animation

I'm making a graph script using canvas, i'm adding animation to a chart but i don't like the way that it's look, i use setInterval for X function adding height to a rectangle to make a bar chart for example, but i wanna an animation more fluid, is another way to do an animation?

no no no. There are three ways to create an animation with JS:
setTimeout()
setInterval()
requestAnimationFrame
options #1 and #2 are old-school ways of creating animations. option #3 is a newer spec and yields the smoothest animations because the browser itself is dynamically controlling the FPS. Here's an awesome shim created by Paul Irish that creates a requestAnimFrame wrapper to handle all browser implementations:
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
// usage:
// instead of setInterval(render, 16) ....
(function animloop(){
render();
requestAnimFrame(animloop, element);
})();

I'm assuming that you have rectangles of initial height 0, and you're increasing height per interval until you reach a set point... and that you want to make the animation "smoother"?
To make it more fluid, you just lower the 2nd parameter of setInterval [delay] so that the first parameter [function to call] is called more...
In addition, you can add a tween with a slowdown at the end by using the formula
rect.h = (rect.h*N+targetHeight)/(N+1)... where N > 1...
So that initially, the bar grows by a lot and then eventually slows down growth to the target height.

Related

How to test react konva from cypress?

I have rectangles as screens on canvas using react-konva. How to test clicking the screens rectangle on testing tools like cypress that uses DOM Element to select the target element?
I am seeing that this is impossible, unless by creating screens DOM Element for testing purpose apart of what currently exists on the canvas. Somehow this will take a lot of time and cumbersome too.
So I wonder if we have a way to work around this to test objects that are drawn inside canvas itself?
Take a look into Konva testing code. Like https://github.com/konvajs/konva/blob/master/test/functional/MouseEvents-test.js
You can emulate clicks with this code (from here):
Konva.Stage.prototype.simulateMouseDown = function(pos) {
var top = this.content.getBoundingClientRect().top;
this._mousedown({
clientX: pos.x,
clientY: pos.y + top,
button: pos.button || 0
});
};
// the use it:
stage.simulateMouseDown({ x: 10, y: 50 });
But you have to find a way to access the stage instance for such testing. And I am not sure it is good in a cypress way, because its API is abstract and DOM-based.
Or you can try to trigger events with cypress:
cy.get(`.container > div`)
.trigger('mousedown', { clientX: x, clientY: y })

Multiple overlapping animations on the same element in svg.js

I want to start a animation on an element while a previous animation is still active. However, calling animate() on the element queues the new animation at the end of the current animation.
For example, consider an animation where an element is being moved to a new position. Now, I also want to make it fade out when it reaches a certain position. The following queues the “once” animation at the end of the move, rather than at 80%.
rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
rect.animate(200).opacity(0);
});
How do I make the element start fading out when it reaches 80% of the move? The API seems to be designed for chaining animations rather simultaneous overlapping animations.
What you are trying to do is a bit more complicated. Unforrtunately its not possible to "hack" into the current animation and add a new animation on the fly.
However what you can do is adding a new property which should be animated:
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.opacity(0);
});
As you will notice that has its own problems because the oopacity immediately jumps to 80%. So this is not an approach which works for you.
Next try: Use the during method:
var morh = SVG.morph(
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.during(function(pos, morphFn, easedPos) {
pos = (pos - 0.8) / 0.2
this.target().opacity(pos)
}
});
We just calculate the opacity ourselves

What is the `e` parameter passed to the d3.js force tick callback function?

below is a normal example of tick function:
function tick(e) {
nodes
.each(cluster(10 * e.alpha * e.alpha));
}
who can tell me the definition of "e"? What properties does it have?
I can't find any description of "e", and what's the meaning of e.alpha. Yes, I used google but with no results.
Thanks for the help you gave below.
I'm copying some code, which use
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.charge(-70)
.gravity(0.1)
.on("tick", tick)
.start();
so it's just the case you guess. I'm new to d3, a skim of force.layout API didn't give me any clue. Thanks for your precious time!
Without the full context of your "normal" function, this is a bit of a guess, but here goes:
tick is used in many contexts within d3. The inlcusion of alpha suggests that this is a force layout tick function, which is called by the force layout object on a tick event, in which case e would be the tick event object.
There is not a lot of documentation about the tick event, as most examples don't use it. If you inspect the source code, you will see
// A rudimentary force layout using Gauss-Seidel.
d3.layout.force = function() { //line 11
var force = {},
event = d3.dispatch("start", "tick", "end");
/* ... */
force.tick = function() { //line 58
// simulated annealing, basically
if ((alpha *= .99) < .005) {
event.end({type: "end", alpha: alpha = 0});
return true;
}
/* code to implement default force layout adjustments */
event.tick({type: "tick", alpha: alpha}); //line 128
};
/* ... */
return d3.rebind(force, event, "on"); //line 305
};
In other words, the tick event is one of three types of custom event created within the d3 source code using the d3.dispatch process. The tick event in particular is dispatched at the end of the internal tick function, and only contains one custom property: the current alpha parameter within the force layout. In order that these events actually go anywhere, the on method of the event dispatcher object is rebound on to the force layout object, so that the user can register listener functions for the custom events.
If all that is way too much d3 internals for you, just focus on these details:
e is a custom event object passed to your tick function every time it is called
e.alpha is the force layout's current alpha value, which by default starts at 0.1 and gets reduced (according to the friction parameter) at each tick until it drops below 0.005 and the layout freezes:
Internally, the layout uses a cooling parameter alpha which controls the layout temperature: as the physical simulation converges on a stable layout, the temperature drops, causing nodes to move more slowly. Eventually, alpha drops below a threshold and the simulation stops completely, freeing the CPU and avoiding battery drain. (From the API wiki for force.start)

Performance considerations using html elements on top of canvas

It is known that if you have html elements (for example a modal window with lists) on top of a flash element you have huge performance issues cause flash cause the browser to repaint the any html on top of it while the flash is animating. I wonder if the same happens if you have html elements on top of an animating canvas element.
I am asking this cause I am building a canvas game and I wonder if it is a good idea to make the GUI (menus, navigation buttons etc) using DOM and not drawing it on canvas.
I just tested using Chromium Version 28.0.1500.45 Mageia.Org 3 (205727) and the elements are NOT repainted while my canvas animates.
I tried this simple box animation with a DIV over it. After that, I profiled my application to see what was happening. I noticed only the canvas was being repainted.
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var GX = 0;
function animate() {
var canvas = document.getElementById('jaja');
var context = canvas.getContext('2d');
// update
GX += 10;
if (GX > 500) GX = 0;
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw stuff
context.beginPath();
context.rect(GX, 10, 100, 100);
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = 1;
context.strokeStyle = 'black';
context.stroke();
requestAnimFrame(function() {
animate();
});
} // request new frame
Following, I tried to make the DIV repaint by selecting the text. This time yes, the DIV repainted.
Here's a screenshot of the profile.
1: We can se the Paint (600x586) being called everytime I do my animation.
2: It called Paint for the DIV element ONLY when I selected the text.
I personally do not believe any other browser would have a different behavior than that.
Yes, putting other DOM elements on top of a canvas element will reduce its performance.
This is because the browser have to do extra clipping when updating canvas / painting.
The canvas need to update 60 times per second to output to screen. If something is on top it needs to be clipped just as many times. If the DOM element is repainted as well will be browser dependent but the performance of the canvas element itself is reduced.
Usually the DOM paints happens in a single thread (but is about to change for most major browsers) so if there is extra load on that thread it will affect everything else being drawn too.
And there is the single-threading of JavaScript which is necessary to use to update canvas. If canvas has reduced performance than the script executing its changes (as well as changes to the DOM) will get hit too.

jQuery Mousemove: trigger on change of 5px

For a number of technical reasons, I'm implementing my own 'draggable' feature on jQuery, rather than using jQuery UI, and I'm using mousedown and mousemove events to listen for the user trying to drag an element.
It works good so far, I would just like to fire up the mousemove event every 5 px of movement, rather than pixel by pixel. I've tried coding a simple:
$('#element').bind('mousemove', function(e) {
if(e.pageX % 5 == 0) {
// Do something
}
});
However, the movement is not stable every 5 pixels, and sometimes if you move the mouse too fast, it will skip several steps. I think this is because when moving the mouse very fast, jQuery will not trigger the event every pixel.
Do you guys know how to trigger the event every 5 pixels?
Thanks a lot,
Antonio
Your code does not take into account where your drag started. e.pageX will just give you page coordinates, not differences. You need to check for the change in distance moved.
This post is pretty relevant.
Here is the basic code:
$(document).mousemove(function(event) {
var startingTop = 10,
startingLeft = 22,
math = Math.round(Math.sqrt(Math.pow(startingTop - event.clientY, 2) +
Math.pow(startingLeft - event.clientX, 2))) + 'px';
$('span').text('From your starting point(22x10) you moved: ' + math);
});
EDIT: Now I think I understand what the OP is talking about. I used the above code to come up with this fiddle. It tracks your current position in relation to the Top Left of the screen and it checks to see if your difference is > 5 pixles.
New script:
var oldMath = 0;
$(document).mousemove(function(event) {
var startingTop = 10,
startingLeft = 22,
math = Math.round(Math.sqrt(Math.pow(startingTop - event.clientY, 2) +Math.pow(startingLeft - event.clientX, 2))) + 'px';
$('#currentPos').text('you are at :' + math);
if(Math.abs(parseInt(math) - oldMath) > 5){
//you have moved 5 pixles, put your stuff in here
$('#logPos').append('5');
//keep track of your position to compare against next time
oldMath = parseInt(math);
}
});​

Resources