given incremental values form 0 to infinite
I'd like to get back a value that increase, then decrease, then increase, then decrease...like a sine
I'm basically trying to get an animation of a ball that goes up and down, based on input values that are only linearly increasing (this value is given by scrolling the page for example to from 0px to TBD +1)
what the math function should I use?
thanks
Well, JavaScript has a sine function:
var y = Math.sin( Math.PI ); // returns 0
Math.sin() takes its argument in radians.
If you want to "slow down" the oscillation, what you have to do is stretch out the input parameter: just divide what you're passing into the sine function by some constant. For example, if x is your input parameter, and you want the animation to go half as fast, do this:
var y = Math.sin( x / 2 );
Also keep in mind that sine goes negative; its range is [-1,1]. So if you want to avoid the negative values, you'll have to add 1 to the output of the function:
var speed = 0.5; // 2=2x speed, 0.5=half speed, etc.
var y = (Math.sin( x * speed ) + 1)/2;
// y will range from [0,1]
See this jsfiddle for a demonstration: http://jsfiddle.net/r8yRN/
Related
I'm trying to plot this type of "binary matrix" graphic:
Disregard the two colors from the sample image; I want to either color a dot blue for, let's say, "complete" values or leave it uncolored/gray for "incomplete" values as a way to track daily task completion for a certain amount of dots/days. The dots represent a day where a task was completed or not completed. Showing the full amount of dots/days gives perspective on % of completion as days go by.
I would like to use a combination of HTML/Javascript and PHP + MySQL. But the hardest part for me is figuring out a good algorithm to render this visualization. Thanks for your help.
Just treat each dot like it's a pixel. Also, imagine that the image has been rotated 90° CCW. Then, you simply draw a square that takes up less room that is allocated to it - this way, you get the separating lines.
Here'e a quick something to have a play with.
A few notes:
0) I just halved your image dimensions
1) 4 pixels and 5 pixels were chosen arbitrarily
2) I didn't bother with setting the colour of the dot - you can
easily do this.
3) I've simply treated the drawing area like a normal top-bottom
bitmap, while your image seems to show that all of the Y values will
be used before the next X value is needed. (This is like a 90° CCW
rotation).
4) I'm addressing the pixels with an X and a Y - perhaps you'd be
more interested in addressing them with a single number? If so, you
could easily write a function that would map two coords to a single
number - the pixels index, if you like.
I.e if an image is 100 x 100, there are 10,000 pixels. You could address them by specifying a number from 0 - 9,999
E.g
function 10k_to_100x100(index)
{
var x = index % 100;
var y = (index / 100).toFixed(0);
plotPixelDot(x, y);
}
X is simply the remainder when dividing by the width
Y is the whole number answer when dividing by the width
Here's a snippet you can try right here on the page:
function byId(id){return document.getElementById(id);}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded()
{
var x, y;
for (y=0; y<20; y++)
{
for (x=0; x<100; x++)
{
drawDot(x, y, 'output');
}
}
}
function drawDot(xPos, yPos, canvasId)
{
var actualX=xPos*5, actualY=yPos*5;
var ctx = byId(canvasId).getContext('2d');
ctx.fillRect(actualX, actualY, 4, 4);
}
<canvas width=558 height=122 id='output'></canvas>
I have a circle in my canvas. The mouse position is calculated in relation to the canvas. I want the circle to move when the mouse is at <=100px distance from it. The minimum distance to start moving is 100px, at 0.5px/tick. It goes up to 2px/tick at 20px distance.
Basically, the closer the mouse is to the circle, the faster the circle should move.
What I have so far moves the circle when distance is less or equal to 100 -- (I'm using easeljs library)
function handleTick() {
distance = calculateDistance(circle, mX, mY);
if (distance<=100) {
circle.x += 0.3;
stage.update();
}
}
What I want
function handleTick() {
distance = calculateDistance(circle, mX, mY);
if (distance<=100) {
circleSpeed = // equation that takes distance and outputs velocity px/tick.
circle.x += circleSpeed;
stage.update();
}
}
So I thought this was a mathmatical problem and posted it on math exchange, but so far no answers. I tried googling several topics like: "how to come up with an equation for a relation" since I have the domain (100, 20) and the range (0.5, 2). What function can relate them?
Thing is I'm bad at math, and these numbers might not even have a relation - I'm not sure what I'm looking for here.
Should I write a random algorithm "circleSpeed = 2x + 5x;" and hope it does what I want? Or is it possible to do as I did - "I want these to be the minimum and maximum values, now I need to come up with an equation for it"?
A pointer in the right direction would be great because so far I'm shooting in the dark.
If I understand it correctly, you want circleSpeed to be a function of distance, such that
circleSpeed is 0.5 when distance is 100.
circleSpeed is 2 when distance is 20.
There are infinity functions which fulfill that, so I will assume linearity.
The equation of the line with slope m and which contains the point (x₀,y₀) is
y = m (x-x₀) + y₀
But in this case you have two points, (x₁,y₁) and (x₂,y₂), so you can calculate the slope with
y₂ - y₁
m = ───────
x₂ - x₁
So the equation of the line is
y₂ - y₁
y = ─────── (x - x₁) + y₁
x₂ - x₁
With your data,
0.5 - 2
y = ──────── (x - 20) + 2 = -0.01875 x + 2.375
100 - 20
Therefore,
circleSpeed = -0.01875 * distance + 2.375
I assume you want a linear relation between the distance and speed?
If so, you could do something like circleSpeed = (2.5 - 0.5(distance/20)).
That would, however set the speed linearly from 0 to 2.5 on the range (100 to 0), but by using another if like this if (distance < 20) circleSpeed = 2 you would limit the speed to 2.0 at 20 range.
It's not 100% accurate to what you asked for, but pretty close and it should look ok I guess. It could possibly also be tweaked to get closer.
However if you want to make the circle move away from the mouse, you also need to do something to calculate the correct direction of movement as well, and your problem gets a tiny bit more complex as you need to calculate speed_x and speed_y
Here is a simple snippet to animate the speed linearly, what that means is that is the acceleration of the circle will be constant.
if distance > 100:
print 0
elseif distance < 20:
print 2
else:
print 2 - (distance -20 ) * 0.01875
Yet other relationships are possible, (other easings you might call them) but they will be more complicated, hehe.
EDIT: Whoops, I’d made a mistake.
I want to move the object in my case its a plane along the shown curve on page scroll step by step taking into consideration the amount of scroll value.firstly the object moves in straight line and then after a point it changes its direction and move in that direction.How to calculate those co-ordinates?
There are two ways you could get this one. I'll try to explain both in detail.
Scenario 1: Simple path like in the question.
Scenario 2: Arbitrary complex path.
Scenario 1:
In this case you can use a simple formula. Let's go with y = -x^2. This will yield a parabola, which has a similar shape as the path in the question. Here are the steps for what to do next (we assume your scrolling element is the body tag and I assume you have jquery):
Get the "y" value of the body using the following code:
var y = $("body").scrollTop();
Plug this value into the formula. I will give 3 examples where y is 0, 100 and 225 respectively.
//y=0
0 = -x^2
-x = sqrt(0)
x = +/- 0
So if we scroll and we are at the top of the page, then x will be zero.
//y=100
100 = -x^2
-x = sqrt(100)
x = +/- 10
The equation yieldsx as either positive of negative x but we only want positive so be sure to Math.abs() the result.
//y=225
225= -x^2
-x = sqrt(225)
x = +/- 15
From this you can see that the further we scroll down the more the object moves to the right.
Now set the "left" css of your object to the calculated value. This should be enough for this method.
Scenario 2
For more complex paths (or even random paths) you should rather put the x-values into an array ahead of time. Lets say you generate the array randomly and you end up with the following x-values:
var xvals = [0, 0.5, 1, 0.5, 0];
We use normalized x-values so that we can later calculate the position independent from screen size. This particular series of values will cause the object to zig-zag across the screen from left to right, then back to left.
The next step is to determine where our scroll position is at relative to the total scroll possibility. Lets say our page is 1000px in height. So if the scoll position is at zero then x = 0. If scroll = 500 then x = screenWidth. If scroll = 250 then x = 0.5 * screenWidth etc.
In the example I won't multiply with screen width for the sake of simplicity. But given the x value this should be simple.
The first thing you might want to get ready now is a lerping function. There is plenty of example code and so on so I trust that part to you. Basically it is a function that looks like this:
function lerp(from, to, prog);
Where from and to are any values imaginable and prog is a value between 0 and 1. If from is 100 and to is 200, a prog value of 0.5 will yield a return of 150.
So from here we proceed:
Get the scroll value as a normalized value
// scrollval = 200
var totalScroll = 1000;
var normScroll = scrollval/totalScroll; // answer is 0.2
Before we get to lerp we first need to get the x-values to lerp from and to. To do this we have to do a sort of lerping to get the correct index for the xvals array:
// normScroll = 0.2
var len = xvals.length; // 5
var indexMax = Math.ceil((len-1) * normScroll); // index = 1
var indexMin = Math.floor((len-1) * normScroll); // index = 0
Now we know the 2 x values to lerp between. They are xvals[0] which is 0, and xvals[1] which is 0.5;
But this is still not enough information. We also need the exact lerping "prog" value:
// We continue from the x indeces
scrollRange.x = totalScroll / len * indexMin; // 0
scrollRange.y = totalScroll / len * indexMax; // 250
var lerpingvalue = (scrollVal - scrollRange.x) / (scrollRange.y - scrollRange.x);// 0.8
Now we finally have everything we need. Now we know we need a value between xvals[0] and xvals[1] and that this value lies at 80% between these two values.
So finally we lerp:
var finalX = lerp(xvals[0], xvals[1], lerpingvalue);// 0.4
Now we know that the x coordinate is at 0.4 of the total screen size.
Trigger these calculations on each scroll event and you should be on your way.
I hope this was clear enough. If you try and try and can't get results and can show how hard you tried then I'll be happy to write a complete index.html sample for you to work from. Good luck!
EDIT: I made a mistake with the lerpingval calculation. Fixed now.
I am doing a small JavaScript animation hoping that the little div can move along a sine wave, and for the moment the horizontal path works fine (just straight line). I am almost sure that my math formula for the Y axis is wrong. I have tried to correct it with some examples I found, but none of them worked for me. In all the possibilities I tried, the Y axis is ignored and the little box just moves in straight line horizontally.
How can I fix this, so the movement goes along a sine wave? I know that it's possible to do it easier with jQuery or using html 5, but I just got wondering what is wrong in my original code... I would prefer to fix this if possible.
function animate() {
xnow = item.style.left;
item.style.left = parseInt(xnow)+1+'px';
ynow = item.style.top;
item.style.top = ynow + (Math.sin((2*Math.PI*xnow)/document.width))*document.heigth;
setTimeout(animate,20);
}
The complete code here:
JSFiddle
I see several problems with your code:
xnow contains a string in this format: ###px You cannot multiply it, so use parseInt() in your Math.sin() call.
Same goes for your code to grab ynow, it needs parseInt().
Better is to use other (global) variables to store the x and y coordinates as numbers. And add px when you update coordinates of the div-element.
When you multiply 2*Math.PI with xnow (which contains only integer numbers), the sin() function will always return 0. So you won't get a sine-like movement. You need to divide xnow by the number of x-steps you want to use to do a complete sine-like movement
Math.sin() returns a value between -1 and +1, so you need to multiply it by an amplitude to see a (more clear) effect.
To keep it as much as you designed it, it would become something like this (takes 50 x-movement steps to do a complete sine and uses an amplitude of 10 pixels):
function animate() {
xnow = parseInt(item.style.left);
item.style.left = (xnow+1)+'px';
ynow = parseInt(item.style.top);
item.style.top = (ynow+Math.sin(2*Math.PI*(xnow/50))*10) + "px";
setTimeout(animate,20);
}
As mentioned: it is much better to use some global variables containing the values (instead of using parseInt() all the time)
See my updated JSFiddle example.
A sin function is in the form of y = a * sin(b*x) + c, where c is the y-midpoint of the function (or the horizontal line across which the function oscillates), where a is the amplitude (maximal y-offset from y = c) and b is the period (number of x = 2*pi segments per wave).
Given that, and that we know a sin wave oscillates from -a to +a, we know our offset (c) should 1) be constant and 2) halfway between our upper and lower bounds. For this we can use
c = document.height / 2;
Your amplitude will be the same value as c, if you want the object to traverse the entire screen. On testing you will find that this makes it go past the bottom of the page, so let's just make it 90%.
a = 0.9 * c;
For a period of 1 for the entire page, you'll need to make b multiply x by a factor such that it will be the fraction of 2*pi. In this case
b = 2*Math.PI/document.width;
On each iteration, there is no need to get the value of ynow, it is a function of xnow. You can do something along the lines of
xnow = parseInt(item.top.left) + 5;
Then calculate the new y with
ynow = c + a * Math.sin(b * xnow);.
Then set the style of the item.
item.style.left = xnow + 'px';
item.style.top = ynow + 'px';
Let me know if anything was unclear. Regards.
You need to use parseInt() on xnow. You also need to add 'px' to the end of the of the number to make into a correctly formatted string.
This code works:
function animate() {
xnow = parseInt(item.style.left);
item.style.left = xnow+1+'px';
item.style.top = 200 + (Math.sin((2*Math.PI*xnow)/200))*document.height+'px';
setTimeout(animate,20);
}
There are a couple errors:
height is misspelled
Parse xnow as an int before taking the sine
Parse ynow as an int before adding it (though it's not actually necessary, see below)
Add "px" to the end of the assignment to item.style.top
The equation could use some tweaking:
I suggest starting with (400 + Math.sin(2*Math.PI*xnow/document.width) * 200) + "px" and then playing around with it. The 400 is the horizontal axis to base the sine wave on. If you use ynow instead of a constant, you get cumulative effects (the wave will be much taller than you intend or the horizontal axis will change over time).
document.width is the width of one full period. The 200 is the peak amplitude (distance from the horizontal to a peak - document.height would push the box off screen in both directions). Plug in this function in place of the current one and then you can play around with the numbers:
function animate() {
xnow = parseInt(item.style.left);
item.style.left = xnow+1+'px';
item.style.top = (400 + Math.sin(2*Math.PI*xnow/document.width) * 200) + "px";
setTimeout(animate,20);
}
I have got a cube in my scene which I want to rotate at a specific start velocity and in a given time interval.
In addition, the cube's end angle should be the same as the start angle.
Therefore, I thought of allowing +- 5% deviation of the time interval.
Here is my current status: http://jsfiddle.net/5NWab/1/.
Don't wonder that is currently working. The problem occurs if I change the time interval, e.g. by '3000': http://jsfiddle.net/5NWab/2/.
The essential move() method of my cube:
Reel.prototype.move = function (delta) {
if (this.velocity < 0 && this.mesh.rotation.x == 0) {
return false;
}
// Create smooth end rotation
if (this.velocity < 0 && this.mesh.rotation.x != 0) {
this.mesh.rotation.x += Math.abs(delta * this.speedUp * 0.5 * this.timeSpan);
if (Math.abs(this.mesh.rotation.x - 2 * Math.PI) < 0.1) {
this.mesh.rotation.x = 0;
}
}
else {
this.mesh.rotation.x += delta * this.velocity;
this.time -= delta;
this.velocity = this.speedUp * this.time;
}
}
The problem is that I cannot think of a solution or method in order to accomplish my topic.
It would not be so complex if I the variable delta would be constant.
It should be around 60fps = 1000/60 because I'm using requestAnimationFrame().
I have also found this question which could help finding the solution.
I think the code should either
slow down the velocity before the actual end is reached.
That should be the case if the final angle is a little bit greater than the desired (start) angle.
or should speed up the rotation speed after the actual end is reached.
That should be the case if the final angle is a little bit smaller than the desired (start) angle.
But what is when the angle is a hemicycle away from the desired one (i.e. 180° or PI)?
In order to clarify my question, here are my knowns and unknowns:
Known:
Start velocity
Time interval
Start angle (usually 0)
I want the cube to have the same start angle/position at the end of the rotation.
Because the FPS count is not constant, I have to shorten or lengthen the time interval in order to get the cube into the desired position.
If you want the rotation to end at a particular angle at a particular time, then I would suggest that instead of continually decrementing the rotation as your current code (2012-09-27) does, set the target time and rotation when you initialise the animation and calculate the correct rotation for the time of the frame recalculation.
So if you were doing a sine-shaped speed curve (eases in and out, linearish in the middle, nice native functions to calculate it), then (pseudocode not using your variables):
//in init
var targetTime = now + animationTime;
// normalize the length of the sine curve
var timeFactor = pi/animationTime;
var startAngle = ...
var endAngle = ...
var angleChange = endAngle - startAngle;
// inside the animation, at some time t
var remainingT = targetTime - t;
if(remainingT <= 0) {
var angle = endAngle;
} else {
var angle = startAngle + cos(remainingT * timefactor) * angleChange;
}
[Edited to add startAngle into andle calculation]
Because the cos function is odd (i.e. symmetric about the origin), as t approaches targetTime, the remainingT approaches zero and we move backward from pi to 0 on the curve. The curve of the sin shape flattens toward zero (and pi) so it will ease out at the end (and in at the beginning. There is an explicit zeroing of the angle at or past the targetTime, so that any jitter in the framerate doesn't just push it into an endless loop.
Here's one possible method, although it's not quite as good as I'd like: http://jsfiddle.net/5NWab/8/
The idea is to decrease the speed gradually, as you were doing, based on the amount of time left, until you get to a point where the amount of distance that the cube has to rotate to reach it's starting rotation (0) becomes greater than or equal to the amount of rotation that can possibly be made given the current velocity and the current amount of time left. After that point, ignore the time left, and slow down the velocity in proportion to the amount of rotation left.
This works fairly well for certain timeSpans, but for others the ending slowdown animation takes a little long.