Raphael JS: Get Point when Animation stops - javascript

I'm programming a small psychological experiment with Javascript and Raphael.
It's my first time using Raphael, and I'm not as good with it, as I want to be ;)
Basically, there is a clock and a small point which is running around it. When the test subject presses the space bar, the point should stop and its absolute X and Y position should be saved.
The animation and the reaction to the space bar press works just fine, but I have my problems with getting the absolute position of the point. I'm using a relative transform to move the point within the clock.
clock.customAttributes.along = function (v) {
var point = p.getPointAtLength(v * len);
return {
transform: "t" + [point.x, point.y] + "r" + point.alpha
};
};
You can look up the experiment here: http://narda.bplaced.net/
If you press the space bar, a new point is drawn at the position which is given by matrix.x(x,y) and matrix.y(x,y).
Is there a way to translate a relative transform into an absolute position? Or can I move the point with an absolute transform instead of a relative?
Thank you for any new approach.

Raphael buries the animation parameters in the object. It takes some nosing around, and I cannot run your code, but I think you'll find the current animation settings here:
console.log(clock._.transform)
It'll be an array, but you should be able to spot the "t" and then two floats might be your x and y.

Just use getBBox()
here is as example:
var m = Raphael("canvas");
var c = m.circle(50,50,1).attr({fill: "#000", stroke: "none"});
c.transform("t150,50")
console.log(c.getBBox().x);
And if you need absolute position add jquery offset
console.log(c.getBBox().x + $("#canvas").offset().left);

Related

What's the idiomatic way to sync transitions so that adjacent shapes move together?

Say I have an arbitrary path like this:
[##########]
I also have a circle like this: o
I want to keep o at the tip of the arbitrary path, so it looks like this:
[##########]o
(Assume o is centered vertically between the top and bottom of the path object) And when the path grows or shrinks, the o should always stay at the tip.
[###############]o
Most importantly, when a transform is applied to the path, the transform should also be applied accordingly to the circle -- they should be in sync when in motion.
I've tried making the circle a path marker, but run into trouble
(a) getting it to only move through the center of the path
(b) getting it to "stick" in the final position
(all examples have it infinitely rotating around the path, like this and this and this)
Calling two transition functions (one for each set of shapes) one after another is usually sufficient, because the time it takes the browser to run through the code is much less than the delay between animation frames.
However, if your animation is sufficiently complex that there is a noticeable lag between the two, or if you are doing a lot of complex calculations that will affect both elements, you could use a custom tween function on one selection, and within it select the other shape and update it (you'll want to select it in your "outer" function, so that your inner function which gets called at every update can just quickly reposition it to match the new value).
Regarding transformations, the easiest way to keep things coordinated is to put both elements in a <g> and transform the group instead of the individual elements.
Putting the ideas together, you could get a transition process something like this:
d3.selectAll("g.groups").transition().delay(time)
.attr("transform", function(d,i){ /* Calculate new transform */ })
.tween("stretch", function(d,i){
/* Select the sub-elements, do all the calculations
then create interpolators for both objects */
var g = d3.select(this);
var path = g.select("path");
var dot = g.select("circle");
var newEndPoint = /*** Calculate final position ***/;
var offset = /*** distance from end point to center of circle ***/;
var pathInterpolator = d3.interpolateString(
path.attr(d),
/*** new path including new end point ***/
);
var dotInterpolator = d3.interpolateObject(
{cx=dot.attr("cx"), cy=dot.attr("cy")},
{cx=newEndPoint.x + offset, cy=newEndPoint.y}
);
return function(t){
/* the function that updates both objects at each tick */
path.attr("d", pathInterpolator(t) );
dot.attr( dotInterpolator(t) );
};
});
How complex your real calculations are will depend on how arbitrary your "arbitrary path" is, of course. Maybe you'll need to calculate both x and y offsets to keep the circle positioned correctly. But that becomes an issue of geometry, not of synchronization. Regardless of what else your path shape does as it transitions, if the end point is an actual point in the path data, it will transition in a direct line, the same as the transition of the circle's coordinates.

Need help for javascript and trigonometry

http://jsfiddle.net/techsin/CxFRC/15/ all chrome only click and drag, up and back arrow.
pastebin.com/gXS1J7zw
The Problem: I can't make c=$('.con') go/translate sideways. I have managed to do front and backward. But I cant workout the formula for sideways.
This is my first attempt. Use arrows keys, forward and backward works are in different function. But leftRight don't.
Code in question: you don't need to worry about rest of the code main logic for right left lies here. I have tried finding the perpendicular angle but it behaves opposite and then opposite of opposite.
First code that works and need no attention:
function forwBack(x){
az+= x*(Math.cos((Math.PI/180)*ry))*Math.cos((Math.PI/180)*rx)*speed;
ax-= x*(Math.sin((Math.PI/180)*ry))*Math.cos((Math.PI/180)*rx)*speed;
ay+= x*(Math.sin((Math.PI/180)*rx))*speed;
}
Now code that doesn't do what I want... translate perpendicular to vector.
function rightLeft(x){
az+= Math.sin(ry*(Math.PI/180))*(Math.cos((rx+90)*(Math.PI/180))*speed*x);
ax-= Math.cos(ry*(Math.PI/180))*(Math.cos((rx+90)*(Math.PI/180))*speed*x);
ay+= Math.sin((rx+90)*(Math.PI/180))*speed*x;
}
x is sign which determines left/right or forward/backward. Speed is hypotenuse here. Pi/180 for deg to radians. And 180/Pi for radians to deg. (rotation around x axis) rx and ry are defined in function ch..and represent rotation of main(.ma) container. rx is defined by vertical movement of mouse.
I don't need to do this, but I want to. I want to make 3d explorer like a hall.. Just for fun. I used to do this kind of stuff in flash like 9 years ago. I was a kid then.
MAIN problem is that sideways is not always -x or +x, it depends on how much outer container has been rotated(around xyz). So if m is rotated 90 around y axis only telling c to move x+ would make c actually look like it's going back or reducing in z.
After trying two days straight I can't fix this.
http://jsfiddle.net/techsin/hGkMj/1/ -- Checking out 3d
http://jsfiddle.net/techsin/hGkMj/6/ -- Checking out 3d
http://jsfiddle.net/techsin/9YjSC/3 I made this to get clear idea of what I was trying to do. Move mouse, & Use arrow keys. Need to click in the preview window.
I found the problem. In your function rightLeft, you are inverting x and y.
I rewrote the function and it is working fine:
function rightLeft(x){
var dAy = 0 ;
var dAz = Math.cos((ry+90)*(Math.PI/180))*speed*x;
var dAx = Math.sin((rx+90)*(Math.PI/180))*speed*x;
console.log("dAx = " + dAx + " dAz = " + dAz + " dAy = " + dAy);
ax-= dAx;
az+= dAz;
ay+= dAy;
}
Hope this helps!

How can I make Raphael.js elements "wiggle" on the canvas?

I'm working on a project that uses SVG with Raphael.js. One component is a group of circles, each of which "wiggles" around randomly - that is, slowly moves along the x and y axes a small amount, and in random directions. Think of it like putting a marble on your palm and shaking your palm around slowly.
Is anyone aware of a Raphael.js plugin or code example that already accomplishes something like this? I'm not terribly particular about the effect - it just needs to be subtle/smooth and continuous.
If I need to create something on my own, do you have any suggestions for how I might go about it? My initial idea is along these lines:
Draw a circle on the canvas.
Start a loop that:
Randomly finds x and y coordinates within some circular boundary anchored on the circle's center point.
Animates the circle from its current location to those coordinates over a random time interval, using in/out easing to smooth the effect.
My concern is that this might look too mechanical - i.e., I assume it will look more like the circle is tracing a star pattern, or having a a seizure, or something like that. Ideally it would curve smoothly through the random points that it generates, but that seems far more complex.
If you can recommend any other code (preferably JavaScript) that I could adapt, that would be great too - e.g., a jQuery plugin or the like. I found one named jquery-wiggle, but that seems to only work along one axis.
Thanks in advance for any advice!
Something like the following could do it:
var paper = Raphael('canvas', 300, 300);
var circle_count = 40;
var wbound = 10; // how far an element can wiggle.
var circleholder = paper.set();
function rdm(from, to){
return Math.floor(Math.random() * (to - from + 1) + from);
}
// add a wiggle method to elements
Raphael.el.wiggle = function() {
var newcx = this.attrs.origCx + rdm(-wbound, wbound);
var newcy = this.attrs.origCy + rdm(-wbound, wbound);
this.animate({cx: newcx, cy: newcy}, 500, '<');
}
// draw our circles
// hackish: setting circle.attrs.origCx
for (var i=0;i<circle_count;i++) {
var cx = rdm(0, 280);
var cy = rdm(0, 280);
var rad = rdm(0, 15);
var circle = paper.circle(cx, cy, rad);
circle.attrs.origCx = cx;
circle.attrs.origCy = cy;
circleholder.push(circle);
}
// loop over all circles and wiggle
function wiggleall() {
for (var i=0;i<circleholder.length;i++) {
circleholder[i].wiggle();
}
}
// call wiggleAll every second
setInterval(function() {wiggleall()}, 1000);
http://jsfiddle.net/UDWW6/1/
Changing the easing, and delays between certain things happening should at least help in making things look a little more natural. Hope that helps.
You can accomplish a similar effect by extending Raphael's default easing formulas:
Raphael.easing_formulas["wiggle"] = function(n) { return Math.random() * 5 };
[shape].animate({transform:"T1,1"}, 500, "wiggle", function(e) {
this.transform("T0,0");
});
Easing functions take a ratio of time elapsed to total time and manipulate it. The returned value is applied to the properties being animated.
This easing function ignores n and returns a random value. You can create any wiggle you like by playing with the return formula.
A callback function is necessary if you want the shape to end up back where it began, since applying a transformation that does not move the shape does not produce an animation. You'll probably have to alter the transformation values.
Hope this is useful!
There is a very good set of easing effects available in Raphael.
Here's a random set of circles that are "given" bounce easing.
Dynamically add animation to objects
The full range of easing effects can be found here. You can play around with them and reference the latest documentation at the same time.
Putting calls in a loop is not the thing to do, though. Use callbacks, which are readily available.

Raphael 2.0 - how to correctly set the rotation point

I don't understand how to rotate my object at a certain point. This is the javascript I have
// create needle
var rsr = Raphael('rsr', '320', '240');
var needle = rsr.path("m 156.74443,870.84631 -2.26177,119.38851
4.38851,0 z"); needle.attr({id: 'needle',parent: 'layer1',fill:
'#ff6600',stroke: '#000000',"stroke-width": '0.61',"stroke-linecap":
'butt',"stroke-linejoin": 'miter',"stroke-miterlimit": '4',"stroke-
opacity": '1',"stroke-dasharray": 'none'});
needle.rotate(0);
needle.transform("t0,-812.36218").data('id', 'needle');
// get needle bounding box
var needleBox = needle.getBBox();
// calculate rotation point (bottom middle)
var x_rotate_point = needleBox.x + (needleBox.width/2);
var y_rotate_point = needleBox.y + needleBox.height;
// rotate needle
needle.attr({rotation: 0}).animate({transform:"r45,"+x_rotate_point
+","+y_rotate_point}, 6000);
// Creates circle at rotation point
var circle = rsr.circle(x_rotate_point, y_rotate_point, 10);
circle.attr("fill", "#f00");
circle.attr("stroke", "#fff");
I have created a dummy circle to check if my coordinates of my center point is correct
and it is, but it doesn't rotate around that point :-/
When I created gauges in different frameworks that always seemed the way to go, but that logic doesn't seem to translate well into Raphael 2.0.
I did google for it and found some entries but the problem seems it is for
older versions which doesn't translate well because a lot of
stuff got changed or is deprecated.
You are setting the center of rotation correctly, but there are some other things going on which are causing some confusion. I was able to get this to work by changing the animation target to:
{transform: needle.attr("transform") + "R45,"+x_rotate_point+","+y_rotate_point}
I think the way you had it, the animation was gradually removing the previous translation while also doing the rotation. This addition allows it to accumulate the transformations. Also, note that I had to switch the 'r' to 'R'. It is not really clear to me what the small 'r' is doing in this example.
Anyway, here is a working demo.
Btw, I also commented out a few rotations that didn't seem to be doing anything.

SVG animation along path with Raphael

I have a rather interesting issue with SVG animation.
I am animating along a circular path using Raphael
obj = canvas.circle(x, y, size);
path = canvas.circlePath(x, y, radius);
path = canvas.path(path); //generate path from path value string
obj.animateAlong(path, rate, false);
The circlePath method is one I have created myself to generate the circle path in SVG path notation:
Raphael.fn.circlePath = function(x , y, r) {
var s = "M" + x + "," + (y-r) + "A"+r+","+r+",0,1,1,"+(x-0.1)+","+(y-r)+" z";
return s;
}
So far, so good - everything works. I have my object (obj) animating along the circular path.
BUT:
The animation only works if I create the object at the same X, Y coords as the path itself.
If I start the animation from any other coordinates (say, half-way along the path) the object animates in a circle of the correct radius, however it starts the animation from the object X,Y coordinates, rather than along the path as it is displayed visually.
Ideally I would like to be able to stop/start the animation - the same problem occurs on restart. When I stop then restart the animation, it animates in a circle starting from the stopped X,Y.
UPDATE
I created a page that demonstrates the issue:
http://infinity.heroku.com/star_systems/48eff2552eeec9fe56cb9420a2e0fc9a1d3d73fb/demo
Click "start" to start the animation.
When you stop and re-start the animation, it continues from the current circle coords in a circle of the correct dimensions.
The problem is that Raphael has no way of knowing that the circle is already part-way along the path. The "start" function means just that -- start an animation. imo it would be broken if it did anything else.
That said, your use case is a valid one, and might warrant another function -- a 'pause' of some sort. Of course, getting that into trunk would take longer probably than you want to wait.
From the Raphael source code, here's what happens when you call 'stop'.
Element[proto].stop = function () {
animationElements[this.id] && animationElements[length]--;
delete animationElements[this.id];
return this;
};
This decrements the total number of animations, and removes that animation from the list. Here's what the 'pause' function might look like:
Element[proto].pause = function () {
animationElements[this.id] && animationElements[length]--;
this._paused_anim = animationElements[this.id];
delete animationElements[this.id];
return this;
};
this saves the animation to be resumed later. then
Element[proto].unpause = function () {
this._paused_anim && (animationElements[this.id]=this._paused_anim);
++animationElements[length] == 1 && animation();
return this;
};
would unpause. Given scoping conditions, these two functions might need to be injected right into the Raphael source code (it's core hacking, I know but sometimes there's no alternative). I would put it right below the "stop" function shown above.
Try that, and tell me how it goes.
====EDIT====
Ok, so it looks like you'll have to modify the "start" attribute of animationElements[this.id]... something like:
this._pause_time = (+new Date) - animationElements[this.id].start;
in the pause, and then
animationElements[this.id].start = (+new Date) - this._pause_time;
on resume.
http://github.com/DmitryBaranovskiy/raphael/blob/master/raphael.js#L3064

Categories