Smoothing normally works as follows:
Some variable v has a target t. Every tick, v makes progress toward t based on a ratio. For example, a ratio of 2:3 or ⅗ will use this formula:
v=(v*3+t*2)/5;
Here you multiply the variables to make the ratio, and then divide it all up to make the smoothing ratio. t can be modified as you want and v will handle smoothing and output.
So you have delta time. You pick a target framerate and go from there. I figured it out on my own so I might be wrong about some terminology but whatever. In one frame, the smoothing will have progressed by ⅖ which means you have a remainder of ⅗. In two frames, you will have a remainder of ⅗×⅗. And I thought: how could I do that fractionally, though? And then I was like "oh wait, you can just make powers of these." So I just got the idea to use insertRatioHere**deltaTime and multiply it by the difference between the current and target state. At first it was something like
v=t-v+(v*(0.6**delta))
but that didn't really work, so I tried a lot of different formulas with small changes, none of which worked. Here's my current test code:
<HTML>
<head>
<title>delta</title>
</head>
<body>
<canvas id=myCanvas width=400 height=300 style="background-color:white;"></canvas>
<script>
var ctx=myCanvas.getContext("2d");
var time={first:new Date(),now:new Date(),delta:new Date()};
time.since=time.now-time.first;
time.delta=time.delta.getTime();
const frame=1000/60;
var lag=100;//change between 0, frame, 100 and other values when desired
var boxA={xPos:200,yPos:150,xAccel:0,yAccel:0,xSpeed:0,ySpeed:0,speed:0};
var boxB={xPos:200,yPos:150,xSpeed:0,ySpeed:0,speed:0};
var keysDown=[];
var keyBind={upA:{key:87,on:false},leftA:{key:65,on:false},
downA:{key:83,on:false},rightA:{key:68,on:false},
upB:{key:38,on:false},leftB:{key:37,on:false},
downB:{key:40,on:false},rightB:{key:39,on:false}};
var varLog=[];
addEventListener("keydown",whenKeyDown);
addEventListener("keyup",whenKeyUp);
tick=setInterval(tickBoxA,lag);
tick=setInterval(tickBoxB,frame);
function tickBoxA()
{time.now=new Date();
time.delta=time.now-time.first-time.since;
time.since=time.now-time.first;
if(keysDown.includes(keyBind.upA.key)){
if(!keyBind.upA.on){keyBind.upA.on=true}
}else{keyBind.upA.on=false}
if(keysDown.includes(keyBind.leftA.key)){
if(!keyBind.leftA.on){keyBind.leftA.on=true}
}else{keyBind.leftA.on=false}
if(keysDown.includes(keyBind.downA.key)){
if(!keyBind.downA.on){keyBind.downA.on=true}
}else{keyBind.downA.on=false}
if(keysDown.includes(keyBind.rightA.key)){
if(!keyBind.rightA.on){keyBind.rightA.on=true}
}else{keyBind.rightA.on=false}
boxA.xAccel=(keyBind.rightA.on-keyBind.leftA.on)*3*((keyBind.upA.on-keyBind.downA.on!=0)?Math.sin(45):1);
boxA.xSpeed=boxA.xAccel-boxA.xSpeed+(boxA.xSpeed*0.9**(time.delta/frame));
//boxA.xSpeed=boxA.xAccel+boxA.xSpeed-(boxA.xSpeed*0.9**(time.delta/frame));
//boxA.xSpeed=boxA.xAccel+(boxA.xSpeed-boxA.xAccel*0.9**(time.delta/frame));
//boxA.xSpeed=boxA.xAccel+(boxA.xAccel-boxA.xSpeed*0.9**(time.delta/frame));
boxA.yAccel=(keyBind.downA.on-keyBind.upA.on)*3*((keyBind.rightA.on-keyBind.leftA.on!=0)?Math.sin(45):1);
boxA.ySpeed=boxA.yAccel-boxA.ySpeed+(boxA.ySpeed*0.9**(time.delta/frame));
boxA.speed=Math.sqrt((boxA.xSpeed**2)+(boxA.ySpeed**2));
boxA.xPos+=boxA.xSpeed*(time.delta/frame);
boxA.yPos+=boxA.ySpeed*(time.delta/frame);
render()}
function tickBoxB()
{if(keysDown.includes(keyBind.upB.key)){
if(!keyBind.upB.on){keyBind.upB.on=true}
}else{keyBind.upB.on=false}
if(keysDown.includes(keyBind.leftB.key)){
if(!keyBind.leftB.on){keyBind.leftB.on=true}
}else{keyBind.leftB.on=false}
if(keysDown.includes(keyBind.downB.key)){
if(!keyBind.downB.on){keyBind.downB.on=true}
}else{keyBind.downB.on=false}
if(keysDown.includes(keyBind.rightB.key)){
if(!keyBind.rightB.on){keyBind.rightB.on=true}
}else{keyBind.rightB.on=false}
boxB.xSpeed=(boxB.xSpeed*9+(keyBind.rightB.on-keyBind.leftB.on)*3*((keyBind.upB.on-keyBind.downB.on!=0)?Math.sin(45):1))/10;
boxB.ySpeed=(boxB.ySpeed*9+(keyBind.downB.on-keyBind.upB.on)*3*((keyBind.rightB.on-keyBind.leftB.on!=0)?Math.sin(45):1))/10;
boxB.xPos+=boxB.xSpeed;
boxB.yPos+=boxB.ySpeed;
render()}
function whenKeyDown(keyDownEvent){
check=keyDownEvent.keyCode;
if(keysDown.findIndex(isSame)==-1)
{keysDown.push(keyDownEvent.keyCode)}}
function whenKeyUp(keyUpEvent){
check=keyUpEvent.keyCode;
keysDown.splice(keysDown.findIndex(isSame),1)}
function isSame(num){return num==check}
function render()
{ctx.clearRect(0,0,400,300);
ctx.fillStyle="#F00";
ctx.fillRect(boxA.xPos,boxA.yPos,20,20);
ctx.fillStyle="#0F0";
ctx.fillRect(boxB.xPos,boxB.yPos,20,20);
varLog.length=0;
varLog.push("Time: "+time.since);
varLog.push("Delta: "+time.delta);
varLog.push("Frame accuracy: "+time.delta/frame);
varLog.push("Standard framerate is 60FPS");
varLog.push("Red box target X: "+boxA.xAccel);
varLog.push("Red box current X: "+boxA.xSpeed);
varLog.push("Red box target Y: "+boxA.yAccel);
varLog.push("Red box current Y: "+boxA.ySpeed);
varLog.push("Red box speed: "+boxA.speed);
varLog.push("The red box is trying to handle smooth");
varLog.push("acceleration in delta time");
varLog.push("and yeah I messed up");
varLog.push("Green box speed X: "+boxB.xSpeed);
varLog.push("Green box speed Y: "+boxB.ySpeed);
varLog.push("Green box speed: "+boxB.speed);
varLog.push("The green box works as desired at 60FPS");
varLog.push("Keys pressed: "+keysDown);
varLog.push("Red=WASD, green=↑←↓→");
ctx.font="15px Courier";
ctx.fillStyle="#000";
for(run=0;run<varLog.length;run++){ctx.fillText(varLog[run],0,(run+1)*15)}}
</script>
</body>
</html>
I'm building a small UI which provides users progress of downloading or loading certain information. Here is the codes so far
http://jsfiddle.net/pge1wukj/4/
var s = Snap("svg");
var movepath = s.select("#movePath").attr({
"fill":"none"
});
var dapath = s.select("#dapath").attr({
stroke: "#cdcdcd",
"stroke-width": 5,
});
var dapoints = [242,334.5, 372,334.5, 372,390, 320.5,390.5 ,308.5,421.5 ,293.5,391.5 ,242,391]
var circle = s.select("circle");
var poly = s.select("polygon");
$("a").click(function(){
circle.animate({
opacity: 0
},100);
poly.polyAnimate(dapoints,100,mina.linear,function(){
moveAlongPath(poly,{x:308,y:421},s.halfArc(308,421,135,382,50,0),100);
});
dapath.animate({
d:"M135,382.5c0,52.159,85,79.031,170.001,79.498 C 390.3,462.466,475.598,436.344,475,382.5",
},100,function(){
dapath.animate({d:"M135,382.5c0,0,85.999-0.467,171,0c85.299,0.468,169,0,169,0"
},100,function(){
dapath.animate({
d:"M135,382.292c0,0,85.999-22.759,171-22.292c85.299,0.468,169,22.292,169,22.292"
},100,function(){
dapath.animate({
d:"M136,382.415c0,0,90.999,13.118,176,13.585c85.299,0.468,164-13.585,164-13.585"
},100,function(){
dapath.animate({
d:"M135,382.5c0,0,85.999-0.467,171,0c85.299,0.468,169,0,169,0"
},500,mina.bounce,function(){
var pathclone = dapath.clone().attr({
stroke: "blue",
strokeDashoffset: 500,
strokeDasharray: 500
});
var datext = s.text(100,330,"90").attr("style","text-align: center");
var banner = s.group(poly,datext);
moveAlongPath(banner,{x:136,y:382.415},movepath,3600);
var tick = 0;
var interval = setInterval(function(){
tick += 1;
var red = Math.random()*255;
var blue = Math.random()*255;
var green = Math.random()*255;
var hex = Snap.rgb(red,green,blue);
var dadatext = $("text").text(tick+" %")
if(tick % 10 == 0){
dadatext.attr({"font-size":"30px","fill":hex});
};
if (tick >= 100){clearTimeout(interval)};
},36);
pathclone.animate({
"stroke-dashoffset":0,
},5300);
var paths = Snap.set().push(dapath,pathclone);
paths.animate({
d:"M135,382.5c0,0,30,17,42,17c10,0.208,298-17,298-17"
},300,function(){
paths.animate({
d:"M135,382.5c0,0,287,17.208,297,17c12,0,43-17,43-17"
},2900,function(){
paths.animate({d:"M135,382.5c0,0,287,17.208,297,17c12,0,43-17,43-17"},100,function(){
paths.animate({
d:"M135,382.5c0,0,85.999-0.467,171,0c85.299,0.468,169,0,169,0"
},200,function(){
$("a").off("click");
/* End of animation */
/* Ready for next transformation*/
banner.animate({
transform: "rotate(180deg)"
},200);
})
})
});
})
});
});
})
})
});
});`
At the end of the animation, the banner should rotate 180 deg on the sharp point. However it doesn't animate as i expected. Is there any solution to this ? transforming animation is intimidating and i don't fully understand it....
I think what you are missing is that you need to provide the original transform to include, otherwise it will assume you are just replacing that transform.
So with this line...
banner.animate({ transform: 'rotate(180)' },200);
What this really means is, I'm going to overwrite any current transforms and animate to a new one of rotate(180).
What you probably want is...keep any existing transforms, and now rotate(180) as well.
So you probably want something more like this...transform() with no parameters will give us the existing transform. So we can combine.
transform -> existingtransform then apply additional transform
This would look like the following.
banner.animate({ transform: banner.transform() + " s-1,1" },200);
I'm not quite sure of the rotation effect you are after (did you mean it to go upside down or back to front?), but 's-1,1' may have been what you were thinking of.
jsfiddle example
Edit: jsfiddle with alternate rotation.
Note, for this, you need to take into account 'where' the polygon is, in relation to the group, as its offset as you have moved it (and then also moved the group).
Edit: The rotation center point is quite difficult, as we have the polygon points offset (not centred around 0) and then translated. Also the group they are in is translated, so you've kinda of got 3 things going on.
To try and help understand getting the actual points, I have rewritten the rotation animation.
banner.animate({ transform: banner.transform() + "r180," + banner.getBBox(1).cx + ',' + banner.getBBox(1).y2 },200);
We get the bounding box, which calculates the centre for us. The pivot point is mid length cx, and the lowest y point is y2.
I suspect there is an easier way to get the whole thing working to reduce the transform complexity in the overall code, but there's a bit too much to break down for a question here.
jsfiddle with getBBox
I am trying to figure out how to sequence the train animation so I can offset and rotate each wagon in turn around curves, for example, in Route_51 (click Test>Run[twice]) in this display. Needs Chrome or other HTML5 compliant browser.
Here is my so far 'non-complying' code (using KineticJs):
function animate(nr,path,incr,train,dirX,dirY){
var steps,offsetX,offsetY,count,a;
steps = Math.round(path[nr][2] / incr);
offsetX = path[nr][2]/steps;
offsetY = path[nr][3]/steps;
count = 0;
stage.onFrame(function(frame){
layer = train[0].getLayer();
if(count < steps){
for(a=0; a<train.length; a+=1){
incrX = train[a].getX() + offsetX * -dirX;
incrY = train[a].getY() - offsetY * -dirY;
train[a].setX(incrX);
train[a].setY(incrY);
}
layer.draw();
count += 1;
}
else{
stage.stop();
nr += 1;
if(path[nr]){
animate(nr,path,incr,train,dirX,dirY);
}
}
});
stage.start();
}
I don't seem to be able to grasp the logic (getting old).
All help appreciated. Thanks.
It seems a certain amount of time has to pass before some kind of logic emerges.
In this case it was that each loco/wagon needed its own fully incremented path for the starts and optionally finishes to be staggered. Here is a screenshot of the train in motion with the "normal" scale view inset. Room for improvement of course especially with curve coordinates.
For the animation visit http://glasier.hk and follow the KJS link.
I'm trying to animate triangle (think, needle of an angular gauge) such that it rotates at a given point (see the red dot).
var svg = Raphael("container",400,400),
triangle = svg.path("M210 200L190 200L200 100Z").attr({fill:"#000"}),
circle = svg.circle(200,200,5).attr({fill:"#f00"});
// to animate ??
triangle.animate({rotation:100,cx:200,cy:200},1000,'<>');
// doesn't work
JSFiddle Example
I can rotate (without animating) along that center fine:
// to rotate with center point 200,200, works fine
triangle.rotate(80,200,200);
But I can't for the life of me figure out how to animate the rotation such that it rotates around that point. It always seems to rotate at the center of the path.
Any help?
Since Raphael.js version 2.0
To animate simple rotation you can use:
yourTriangle.animate({transform: "r" + 15}, 2000);
Where:
r = rotation
15 = angle in degrees
2000 = time in milliseconds.
To animate rotation with given center point
You need to specify center coordinates:
yourTriangle.animate({transform: "r60" + "," + centerX + "," + centerY}, 2000);
JSFiddle example
So you have to use string as an object property: {transform: "r15"} or {transform: "r15, centerX, centerY"}.
Try this:
triangle.animate({rotation:"300 200 200"},1000,'<>');
To rotate a path around a given point, e.g. the end of a line, use this:
rapahel.path.animate({transform: "r60,100,100"}, 1000, "<>");