Real time of SVG-Animation is different from animation time set - animation

I have a question about the performance of SVG animations.
When I set de duration of an animation of an rectangle which should move frome one coordinate to another, the real time the animation takes is different from the time which I set in the animation. If the system/browser is very busy the real time the animation takes increases. I am working with the SVG-Library Raphael.js. Here you can see a code sample in which I tried to illustrate the problem:
var paper = new Raphael("paperDiv");
var rectangle = paper.rect(200,200,30,10);
rectangle.attr({
x: 50,
y: 50
});
window.onload = function(){
var d = new Date();
var datum = d.getTime();
rectangle.animate({x: 500, y: 50}, 1000, "linear", function(){
d = new Date();
alert("time the animation should take: " + 1000 + "; time the animation really takes: " + (d.getTime() - datum));
});
}
Is there any way to set a real animation time?
Many thanks for your answers!

Related

Three.js and Tween.js, chained animation with rotations does not work

I'm trying to move a cube first towards to the camera, then turn left, and then move forward, to the right side of the screen. I'm building a maze where a turtle will move forward and rotate and I want to use tweenjs for the animations.
A fiddle is here https://jsfiddle.net/edapx/o6mvg0d5/1/
As you see, the first two animation are executed, but the last one does not move the cube as expected. I'm chaining the animations like this:
function testChained(){
var radians = 90 * THREE.Math.DEG2RAD;
var pos_copy_a = new THREE.Vector3().copy(mesh.position);
var target_a = pos_copy_a.add(new THREE.Vector3(0.0, 0.0, 50.0));
var rotationMatrix_a = new THREE.Matrix4().makeRotationY(mesh.rotation.y);
target_a.applyMatrix4(rotationMatrix_a);
var a = new TWEEN.Tween(mesh.position).to(target_a, 900);
var target_b = {y:mesh.rotation.y + radians};
var b = new TWEEN.Tween(mesh.rotation).to(target_b, 900);
var pos_copy_c = new THREE.Vector3().copy(mesh.position);
var target_c = pos_copy_c.add(new THREE.Vector3(0.0, 0.0, 50.0));
var rotationMatrix_c = new THREE.Matrix4().makeRotationY(mesh.rotation.y);
target_c.applyMatrix4(rotationMatrix_c);
var c = new TWEEN.Tween(mesh.position).to(target_c, 900);
a.chain(b);
b.chain(c);
a.start();
};
Am I using tween.js wrong?
This is, because you set target_c before all animations start. So, target_c is the same as target_a. When animation c starts, the mesh is at position 0 0 50, but target_c is 0 0 50, too. So, you need to set target_c after animation a has completed. Animation c relies on the "result" of the previous ones, I guess, chaining like isn't that easy.
In jsfiddle, I just just wrapped the instantiation of animation c into the complete callback of animation b to make it work: https://jsfiddle.net/327bcLgp/
EDITED Question
What I'm looking for it is called is animation with relative values. https://github.com/tweenjs/tween.js/blob/master/docs/user_guide.md#relative-values It works when piling up rotations, but mixing rotation and translation is tricky. Here an updated fiddle that still does not works https://jsfiddle.net/edapx/o6mvg0d5/5/
EDITED Answer
It's not just a string (String(distance_a.x)), you need to prefix it with a plus sign to make it relative: '+' + distance_a.x;
And I wouldn't use mesh.rotation.y to rotate the vector. That's the same as I described above. When you set set rotationMatrix_c, mesh.rotation.y will be still 0. Instead, use fix values.
var radians = 90 * THREE.Math.DEG2RAD;
...
var rotationMatrix_c = new THREE.Matrix4().makeRotationY(radians);
var distance_c = new THREE.Vector3(0.0, 0.0, 50.0);
distance_c.applyMatrix4(rotationMatrix_c);
var target_c = {
x: '+' + distance_c.x,
y: '+' + distance_c.y,
z: '+' + distance_c.z
};
https://jsfiddle.net/o6mvg0d5/6/
If you have more rotations, you need to sum up the radians value, e.g. radians += 90 * THREE.Math.DEG2RAD;

Moving a body to a specific position

I understand that I can use body.position.set(x, y, z) to instantaneously move a body, but how can I move it smoothly in an animated manner where it's movement will adhere to the physics and collide with any other bodies on its journey? Using body.velocity.set(x, y, z) will set its velocity, and using body.linearDamping = v, will provide some friction/resistance... but it's still not good enough to allow me to specify exactly where I want the body to stop.
It sounds like you're looking for a kinematic body. With kinematic bodies, you have full control over the movement, and it will push away other bodies in its path. However, the body has infinite mass and is not affected by other bodies colliding with it.
Start off by defining the start and end positions of your body.
var startPosition = new CANNON.Vec3(5, 0, 2);
var endPosition = new CANNON.Vec3(-5, 0, 2);
var tweenTime = 3; // seconds
Then create your kinematic body. In this example we'll add a Box shape to it.
var body = new CANNON.Body({
mass: 0,
type: CANNON.Body.KINEMATIC,
position: startPosition
});
body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
world.add(body);
Compute the direction vector and get total length of the tween path.
var direction = new CANNON.Vec3();
endPosition.vsub(startPosition, direction);
var totalLength = direction.length();
direction.normalize();
The speed and velocity can be calculated using the formula v = s / t.
var speed = totalLength / tweenTime;
direction.scale(speed, body.velocity);
For each update, compute the tween progress: a number between 0 and 1 where 0 i start position and 1 is end position. Using this number you can calculate the current body position.
var progress = (world.time - startTime) / tweenTime;
if(progress < 1){
// Calculate current position
direction.scale(progress * totalLength, offset);
startPosition.vadd(offset, body.position);
} else {
// We passed the end position! Stop.
body.velocity.set(0,0,0);
body.position.copy(endPosition);
}
See full code below. You can duplicate one of the cannon.js demos and just paste this code.
var demo = new CANNON.Demo();
var postStepHandler;
demo.addScene("Tween box",function(){
var world = demo.getWorld();
// Inputs
var startPosition = new CANNON.Vec3(5, 0, 2);
var endPosition = new CANNON.Vec3(-5, 0, 2);
var tweenTime = 3; // seconds
var body = new CANNON.Body({
mass: 0,
type: CANNON.Body.KINEMATIC,
position: startPosition
});
body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
world.add(body);
demo.addVisual(body);
if(postStepHandler){
world.removeEventListener('postStep', postStepHandler);
}
// Compute direction vector and get total length of the path
var direction = new CANNON.Vec3();
endPosition.vsub(startPosition, direction);
var totalLength = direction.length();
direction.normalize();
var speed = totalLength / tweenTime;
direction.scale(speed, body.velocity);
// Save the start time
var startTime = world.time;
var offset = new CANNON.Vec3();
postStepHandler = function(){
// Progress is a number where 0 is at start position and 1 is at end position
var progress = (world.time - startTime) / tweenTime;
if(progress < 1){
direction.scale(progress * totalLength, offset);
startPosition.vadd(offset, body.position);
} else {
body.velocity.set(0,0,0);
body.position.copy(endPosition);
world.removeEventListener('postStep', postStepHandler);
postStepHandler = null;
}
}
world.addEventListener('postStep', postStepHandler);
});
demo.start();
You need to use a physics library for this, such as Physijs. It works easily with Three.js. Googling for "Physijs Three.js" will provide examples.

Cancel a draggable 'dragmove' on kineticjs?

I have to set a limit for the movement of a circle when the coordinate 'y' is on the pixel 'y-15', on a specific coordinate (x,y). I've tried to use 'draggable=true' with the event 'mouseup' and 'mousedown', but I can't get the results that I need. Can someone help me, please?
My code is:
circle.on('mouseover',function(){
this.setAttr('draggable',true);
});
circle.on('dragmove',function(e){
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
if(y < y2- 15){
//do anything
}
else{
this.setAttr('draggable',false);
}
});
If I understand correctly, what you want is dragBoundFunc.
See this tutorial:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-bounds-tutorial-with-kineticjs/
// bound below y=50
var blueGroup = new Kinetic.Group({
x: 100,
y: 70,
draggable: true,
dragBoundFunc: function(pos) {
var newY = pos.y < 50 ? 50 : pos.y;
return {
x: pos.x,
y: newY
};
}
});
You'll just have to modify your code to look for y-15 when you grab your mouse coordinates, and set the dragBoundFunc to reflect the coordinates you clicked on.
If you're trying to limit the y-axis on a known fixed y value, then this becomes even easier, exactly like how the tutorial shows you, except you just have to change the coordinate that you want to limit Y to.
Check this one as a quick reference too:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-shapes-horizontally-or-vertically-tutorial/

rotate to mouse kinetic js

I looked at many examples but so far nothing worked. I want the circle to rotate on mousemove and it is rotating centered, so no problems so far. But, it does a 180 jump when I pass half of the stage. So everything is fine till I pass the half of the stage, clearly I'm missing something. Math.atan2 gives me an error: the circle jumps to the (0,0) of the stage.
Please help I really need this badly.
Thanks in advance!
new Kinetic.Tween({
node: cGrad1,
x: stage.getWidth()/2,
y: stage.getHeight()/2,
duration: 1,
opacity:1,
easing: Kinetic.Easings.EaseInOut
}).play();
clickO.on('mousemove', function() {
for(var n = 0; n < 16; n++) {
var shape = cGradlayer1.getChildren()[n];
var stage = shape.getStage();
var mousePos = stage.getMousePosition();
var x = mousePos.x - shape.getPosition().x;
var y = mousePos.y -shape.getPosition().y ;
var degree = (Math.PI)+(Math.atan(y/x));
shape.setAttrs({
rotation: degree
})
cGradlayer1.draw()
}
})
Well this is what I came up with, and hopefully it's close to what you were looking for: jsfiddle
Basically, to calculate the angle you want to rotate to, we need to store two points:
The origin of the shape (centre coordinate)
The coordinate of the mouse click
Once you have that, you can calculate the angle between the two points with a little bit of trigonometry (Sorry if I am not accurate here, Trigonometry is not my strong suit). Calculate the distance between the two points (dx, dy) and then use the trig formula to find the angle in degrees.
layer.on('click', function() {
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
var rectX = rect.getX()+rect.getWidth()/2;
var rectY = rect.getY()+rect.getHeight()/2;
var dx = x - rectX;
var dy = y - rectY;
var rotation = (Math.atan2(dy, dx)*180/Math.PI+360)%360;
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: rotation,
easing: Kinetic.Easings.EaseInOut
});
rotateOnClick.play();
});
EDIT:
Based off the gathered information below (which I came to the same conclusion as) I have updated my fiddle: jsfiddle
As markE mentioned below in the comments, KineticJS does not support "force-clockwise flag", so the rotation always jumps when rotating past 360 in the clockwise direction, or past 0 in the counter clockwise position. Otherwise, we know that the rotation works properly.
So, to fix this manually there are two cases we need to consider:
When we rotate past 360 in the clockwise direction.
When we rotate past 0 in the counter clockwise direction.
And this is the math I used to calculate whether to counter the rotation or not:
var currentDeg = rect.getRotationDeg();
var oneWay = rotation - currentDeg;
var oneWayAbsolute = Math.abs(rotation - currentDeg);
var otherWay = 360-oneWayAbsolute;
if (otherWay < oneWayAbsolute) {
//Take the other way
if (oneWay > 0) {
//Clicked direction was positive/clockwise
var trueRotation = currentDeg - otherWay;
} else {
//Clicked direction was negative/counter clockwise
var trueRotation = currentDeg + otherWay;
}
} else {
//Take the clicked way
var trueRotation = rotation;
}
Basically we want to figure out which way to rotate, by comparing the angle degrees of which direction would be closer, the direction we clicked in, or the opposite way.
If we determined that the otherWay was closer to the currentDeg, then we need to see if the direction we clicked in was in the counter clockwise (negative) or clockwise (positive) direction, and we set the otherWay direction to go in the opposite direction.
And then you can normalise the rotationDeg onFinish event.
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: trueRotation,
easing: Kinetic.Easings.EaseInOut,
onFinish: function() {
trueRotation = (trueRotation+360)%360;
rect.setRotationDeg(trueRotation);
layer.draw();
}
});
You might need to set the x and y position of the Tween as follows:
new Kinetic.Tween({
x: mousePos.x,
y: mousePos.y,
node: shape,
rotation: degree,
easing: Kinetic.Easings.EaseInOut,
duration:1,
}).play();
See this tutorial for reference http://www.html5canvastutorials.com/kineticjs/html5-canvas-stop-and-resume-transitions-with-kineticjs/

kineticjs rotate to mouseposition [duplicate]

I looked at many examples but so far nothing worked. I want the circle to rotate on mousemove and it is rotating centered, so no problems so far. But, it does a 180 jump when I pass half of the stage. So everything is fine till I pass the half of the stage, clearly I'm missing something. Math.atan2 gives me an error: the circle jumps to the (0,0) of the stage.
Please help I really need this badly.
Thanks in advance!
new Kinetic.Tween({
node: cGrad1,
x: stage.getWidth()/2,
y: stage.getHeight()/2,
duration: 1,
opacity:1,
easing: Kinetic.Easings.EaseInOut
}).play();
clickO.on('mousemove', function() {
for(var n = 0; n < 16; n++) {
var shape = cGradlayer1.getChildren()[n];
var stage = shape.getStage();
var mousePos = stage.getMousePosition();
var x = mousePos.x - shape.getPosition().x;
var y = mousePos.y -shape.getPosition().y ;
var degree = (Math.PI)+(Math.atan(y/x));
shape.setAttrs({
rotation: degree
})
cGradlayer1.draw()
}
})
Well this is what I came up with, and hopefully it's close to what you were looking for: jsfiddle
Basically, to calculate the angle you want to rotate to, we need to store two points:
The origin of the shape (centre coordinate)
The coordinate of the mouse click
Once you have that, you can calculate the angle between the two points with a little bit of trigonometry (Sorry if I am not accurate here, Trigonometry is not my strong suit). Calculate the distance between the two points (dx, dy) and then use the trig formula to find the angle in degrees.
layer.on('click', function() {
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
var rectX = rect.getX()+rect.getWidth()/2;
var rectY = rect.getY()+rect.getHeight()/2;
var dx = x - rectX;
var dy = y - rectY;
var rotation = (Math.atan2(dy, dx)*180/Math.PI+360)%360;
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: rotation,
easing: Kinetic.Easings.EaseInOut
});
rotateOnClick.play();
});
EDIT:
Based off the gathered information below (which I came to the same conclusion as) I have updated my fiddle: jsfiddle
As markE mentioned below in the comments, KineticJS does not support "force-clockwise flag", so the rotation always jumps when rotating past 360 in the clockwise direction, or past 0 in the counter clockwise position. Otherwise, we know that the rotation works properly.
So, to fix this manually there are two cases we need to consider:
When we rotate past 360 in the clockwise direction.
When we rotate past 0 in the counter clockwise direction.
And this is the math I used to calculate whether to counter the rotation or not:
var currentDeg = rect.getRotationDeg();
var oneWay = rotation - currentDeg;
var oneWayAbsolute = Math.abs(rotation - currentDeg);
var otherWay = 360-oneWayAbsolute;
if (otherWay < oneWayAbsolute) {
//Take the other way
if (oneWay > 0) {
//Clicked direction was positive/clockwise
var trueRotation = currentDeg - otherWay;
} else {
//Clicked direction was negative/counter clockwise
var trueRotation = currentDeg + otherWay;
}
} else {
//Take the clicked way
var trueRotation = rotation;
}
Basically we want to figure out which way to rotate, by comparing the angle degrees of which direction would be closer, the direction we clicked in, or the opposite way.
If we determined that the otherWay was closer to the currentDeg, then we need to see if the direction we clicked in was in the counter clockwise (negative) or clockwise (positive) direction, and we set the otherWay direction to go in the opposite direction.
And then you can normalise the rotationDeg onFinish event.
var rotateOnClick = new Kinetic.Tween({
node: rect,
duration: 1,
rotationDeg: trueRotation,
easing: Kinetic.Easings.EaseInOut,
onFinish: function() {
trueRotation = (trueRotation+360)%360;
rect.setRotationDeg(trueRotation);
layer.draw();
}
});
You might need to set the x and y position of the Tween as follows:
new Kinetic.Tween({
x: mousePos.x,
y: mousePos.y,
node: shape,
rotation: degree,
easing: Kinetic.Easings.EaseInOut,
duration:1,
}).play();
See this tutorial for reference http://www.html5canvastutorials.com/kineticjs/html5-canvas-stop-and-resume-transitions-with-kineticjs/

Resources