Articulated animation - javascript

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.

Related

Calculate div number with distance

I'm making a carousel like a casino roulette but i can't find the way to know which div number is when i make the animation. I'm trying to calculate by distance when i make the animation loop but i doesn't work
Here's my example
https://codepen.io/anon/pen/xXbpJr?page=1&
var giftamount = 10;
var gw = $('.gift').outerWidth(true);
var giftcenter = gw/2;
var cycle = 7;
var containercenter = $('.boxwrapper').outerWidth(true)/2;
for(var i = 0; i <=5; i++)
{
var giftduplicate = $('.giftwrapper').children().clone(true,true);
$('.giftwrapper').append(giftduplicate);
}
$('.button').click(function(){
var btn = $(this);
btn.hide();
var randomgift = Math.floor((Math.random() * 10) + 1);
var distance = giftamount * gw * cycle + containercenter + (randomgift*gw) - giftcenter;
console.log(distance);
$( ".giftwrapper" ).css({left: "0"});
$('.giftwrapper').animate({left: "-="+distance},10000,function(){
alert('You Won Gift' + randomgift);
btn.show();
});
});
i get the wrong number of div, i tried a lot of combination but it doesn't work
You can try and substitute this for the distance
var distance = giftamount * cycle * gw + (randomgift*gw) - containercenter -24;
The idea is the following: with distance=- containercenter; you would move to be left-aligned with the center of the container.
To that you add a certain number of cycles giftamount * cycle * gw and finally a random number of gift elements ((randomgift*gw)).
I could not figure out where the constant -24 comes from. I hard-coded and it needs to be better defined but I guess it might depend on some margins/approximations/jQuery/CSS/??
Now you should see that the animation always stops at the same point within the gift element (in the middle). To add a random deviation you could ad a small deviation dev (that lets you stay within the gift element) like this:
var dev = Math.random()*(giftcenter+1);
var distance = giftamount * cycle * gw + (randomgift*gw) - containercenter -24 +dev;
Updated demo: https://codepen.io/anon/pen/RLNeBX
If you want to get the prize that is underneath the pointer (the vertical red bar), you actually do not have to compute the distance. Instead, you can make use of a really handy but somewhat less known DOM API method known as elementFromPoint(x, y), where you can obtain a reference to the topmost DOM node under the x,y coordinate of the page.
In order for this to work, x and y will have to correspond to the visual center of the pointer, which can we can simply calculate by using:
var $pointer = $('.rafflebox .pointer');
var pointerX = $pointer.offset().left + $pointer.width() * 0.5;
var pointerY = $pointer.offset().top + $pointer.height() * 0.5;
In jQuery's animation callback, you simply can retrieve the element (aka the prize) underneath this coordinate:
// Hide pointer first, otherwise it will be returned as the topmost element
$pointer.hide();
// Get element from pointer's visual center
var prize = document.elementFromPoint(pointerX, pointerY);
// Show it again
$pointer.show();
Now you have the correct reference to the DOM node, it is up to you to decide what kind of metadata you want to store in the "prize" DOM node. For example, you can embed a HTML5 data- attribute in your HAML:
%img{:src => "http://placehold.it/125x125?text=Prize+#{i}", :data => {:prize => "Prize #{i}"}}
...which simply stores the text Prize (n) (where n is the prize number) in the attribute data-prize, and we can access it later using:
var prize = document.elementFromPoint(pointerX, pointerY);
console.log($(prize).data('prize'));
When we replace part of your code with what I have suggested, you get this:
// Get visual center of pointer
var $pointer = $('.rafflebox .pointer');
var pointerX = $pointer.offset().left + $pointer.width() * 0.5;
var pointerY = $pointer.offset().top + $pointer.height() * 0.5;
$( ".giftwrapper" ).css({left: "0"});
$('.giftwrapper').animate({left: "-="+distance},10000,function(){
// Hide pointer first, otherwise it will be returned as the topmost element
$pointer.hide();
// Get element from pointer's visual center
var prize = document.elementFromPoint(pointerX, pointerY);
// Show it again
$pointer.show();
alert('You Won Gift ' + $(prize).data('prize'));
btn.show();
});
Here is your updated pen with a working example: https://codepen.io/terrymun/pen/dVPdMg
Updated example
There is a very small chance that the pointer will land in between prizes. To prevent this, you will want to use padding instead of margin on the .gift element:
.gift {
// Change margin to padding
padding: 0px 4px;
}
...and perform additional checks on the returned prize node:
// Hide pointer first, otherwise it will be returned as the topmost element
$pointer.hide();
// Get element from pointer's visual center
var $prize = $(document.elementFromPoint(pointerX, pointerY));
// If prize lands on the .gift element instead
if(!$prize.is('img'))
$prize = $prize.find('img')
// Show it again
$pointer.show();
alert('You Won Gift' + $prize.data('prize'));
btn.show();
The pen here is simply a fork of the original solution, but with exaggerated horizontal padding to increase the chance of the pointer landing in between iamges: https://codepen.io/terrymun/pen/rGaJmY

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

CreateJs - Controlling timeline playhead with click & drag

First time posting here, hope I'm doing this right :) I am trying to create a 360 spin in Adobe Animate with createJS, using a sequence of images embedded in a MovieClip. I have managed to get control of the timeline playhead using the code below:-
var start_x;
var startFrame;
var changeDistance;
var travelDistance;
this.threeSixty.addEventListener("mousedown", onMouseDown.bind(this));
this.threeSixty.addEventListener("pressup", onMouseUp.bind(this));
function onMouseDown(e) {
start_x = e.stageX;
startFrame = this.threeSixty.timeline.position;
this.threeSixty.addEventListener("pressmove", onMouseMove.bind(this));
}
function onMouseUp(e) {
this.threeSixty.removeEventListener("pressmove", onMouseMove.bind(this));
}
function onMouseMove(e) {
var changeDistance = e.stageX-start_x;
var travelDistance = startFrame+changeDistance;
if (travelDistance > this.threeSixty.timeline.duration){
this.threeSixty.gotoAndStop(travelDistance % this.threeSixty.timeline.duration);
}else if (travelDistance < 0){
this.threeSixty.gotoAndStop (this.threeSixty.timeline.duration + (travelDistance % threeSixty.timeline.duration));
} else {
this.threeSixty.gotoAndStop(travelDistance);
}
}
The problem is, when you click and drag the image along the X axis, the image sequence only moves forward/backwards by a single frame, as opposed to continuing the image sequence until the either mouse has stopped moving, or the mouse click is released. Any ideas where I might be going wrong here?
Thanks

Laser Animation with TweenJS and EaselJS

So, I'm trying to create a laser effect, similar to the one located at http://map.norsecorp.com/
For this example, I have a 500x500 canvas. The current javascript part of the solution is located below:
function shootLaser(x, y) {
var beam = new createjs.Shape();
beam.graphics.beginFill("red");
beam.graphics.moveTo(0,1.5).lineTo(70,0).lineTo(70,3).closePath();
beam.x = 80;
beam.y = 50;
stage.addChild(beam);
beam.setBounds(0,0,70,3);
createjs.Tween.get(beam,{ loop: true, onChange: beamUpdate })
.to({ x: 400 }, 1000, createjs.Ease.linear());
}
function beamUpdate(e) {
var beam = e.currentTarget.target;
var targetX = e.currentTarget._curQueueProps.x;
if( targetX - beam.x < beam.getBounds().width ) {
beam.scaleX = (targetX - beam.x) / targetX;
} else {
beam.scaleX = 1;
}
}
This draws the line the way that I want to. However the scaleX method doesn't quite work (not even close really, it just gets extremely small very fast).
The problem is that I can't find a way to shrink the "laser" once it hits it's target. If I shoot it from 0px to 250px. It should hit 250px and begin shrinking until the 250th pixel has "consumed" it for lack of a better term. Any help is greatly appreciated.
P.S. I'm also open to doing this with other libraries or tools. I just haven't found them yet.

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/

Categories

Resources