jQuery "slime menu" - pixel perfect rounding - javascript

I've successfully programmed my jQuery slime menu, and now it works beautiful. My only problem is that I move the arrow and the ending circle (the icon) of the slime separately, so if you move the cursor too away from the starting point, it will not connect to the slime precisely. It must be a rounding issue. Here's an image of what I'm talking about:
And here's the fiddle I'm working on:
http://jsfiddle.net/41Lcj653/4/
EDIT (Working): http://jsfiddle.net/41Lcj653/7/
Can anyone help with this issue? I use CSS transformations, and JS for update the CSS rules in each step.
The JS part of the code:
$(function(){
$(window).mousemove(function(e){
moveslime(e);
});
});
function moveslime(e){
var rad = $('.arrow').height() / 2;
var ofs = $('.arrow').offset();
var mPos = {x: e.pageX-25-rad, y: e.pageY-25-rad};
var dir = Math.round( -(Math.atan2(mPos.x, mPos.y)) / (Math.PI/180) );
$('.arrow').css('transform','rotate('+dir+'deg)');
$('.arrow').css('border-width','0 25px '+pdist(0, 0, mPos.x, mPos.y)+'px 25px');
$('.bubble').css('left', mPos.x+'px').css('top', mPos.y+'px');
}
function pdist(x1,y1,x2,y2) {
return Math.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2));
}

the reason you see this 'not precisely connected' is that rotate goes in 360 steps (the 360 degrees of a circle). when you are near the center the effect can be neglected, but the more you get away from the center (increasing circle circumference!) the visible gap between the degrees increases.
Maybe try this with svg?
actually you can use float numbers a degree .. so dont round the deg!
https://jsfiddle.net/41Lcj653/5/
// without math.round
var dir = -(Math.atan2(mPos.x, mPos.y)) / (Math.PI/180);

Some jQuery method like height rounds decimals. You could try this:
$(".arrow")[0].getBoundingClientRect().height
More information: https://developer.mozilla.org/es/docs/Web/API/Element/getBoundingClientRect

Related

Ratio smoothing with delta time in JavaScript is buggy

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>

Multistage of svg transform animation with Snap SVG

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

Constrain jquery draggable to stay only on the path of a circle

So Im trying to make something where the user is able to drag planets along their orbits and it will continuously update the other planets. Ideally I would like this to work with ellipses too.
So far I can drag an image node with jquery and check/change the coordinates, but i cannot update the position reliably while the user is dragging the object. I know there is an axis and a rectangle containment for draggable but this doesn't really help me.
I have a site for calculating planetary orbits http://www.stjarnhimlen.se/comp/ppcomp.html and a formula i think should help me if i can figure out how to constrain the draggable object with coordinate checks Calculating point on a circle's circumference from angle in C#?
But it seems like there should be an easier way to have a user drag a sphere along a circular track while it updates coords for other spheres
here's what i have so far. It's not much
http://jsfiddle.net/b3247uc2/2/
Html
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
Js
var $newPosX = 100,
$newPosY = 100;
//create image node
var x = document.createElement("IMG");
x.src = "http://upload.wikimedia.org/wikipedia/commons/a/a4/Sol_de_Mayo_Bandera_Argentina.png";
x.width = 100;
x.height = 100;
x.id = "sun";
x.hspace = 100;
x.vspace = 100;
document.body.appendChild(x);
//coords
var text = document.createTextNode($newPosX + " " + $newPosY);
document.body.appendChild(text);
//make sun draggable and update coords
$("#sun").draggable({
drag: function (event, ui) {
$newPosX = $(this).offset().left;
$newPosY = $(this).offset().top;
}
});
//0 millisecond update for displayed coords
setInterval(check, 0);
function check() {
//tries to constrain movement, doesn't work well
/*
if ($newPosX > 300) {
$("#sun").offset({
left: 200
});
}
*/
text.nodeValue = ($newPosX + " " + $newPosY);
}
Edit:
I found this and am trying to modify it to suit my purposes but have so far not had luck.
http://jsfiddle.net/7Asn6/
Ok so i got it to drag
http://jsfiddle.net/7Asn6/103/
This is pretty close to what I want but can be moved without being clicked on directly
http://jsfiddle.net/7Asn6/104/
Ok final edit on this question. This one seems to work well and have fixed my problems. I would still very much like to hear peoples implementation ideas or ideas to make it work smoother.
http://jsfiddle.net/7Asn6/106/

Articulated animation

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.

Raphael.js Animate the rotation of a path with a given center point

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, "<>");

Categories

Resources