Reputation: 9830
I am making a scrollTo
functionality in my website, and basing it on this answer. Because I don't want to just copy and paste the code, I'm trying to understand it. I was able to understand everything (took me 2 days!) besides for the math part in the scrollToX function
.
element.scrollTop = xFrom - (xFrom - xTo) * motion(t01);
t01 += speed * step;
I understand the actual math, but I don't understand why it works. Why does that math make the scroll animation?
document.getElementsByTagName('button')[0].onclick = function() {
scrollTo(0, 1000);
}
// Element to move, time in ms to animate
function scrollTo(element, duration) {
var e = document.documentElement;
if (e.scrollTop === 0) {
var t = e.scrollTop;
++e.scrollTop;
e = t + 1 === e.scrollTop-- ? e : document.body;
}
scrollToC(e, e.scrollTop, element, duration);
}
// Element to move, element or px from, element or px to, time in ms to animate
function scrollToC(element, from, to, duration) {
if (duration <= 0) return;
if (typeof from === "object") from = from.offsetTop;
if (typeof to === "object") to = to.offsetTop;
scrollToX(element, from, to, 0, 1 / duration, 20, easeOutCuaic);
}
function scrollToX(element, xFrom, xTo, t01, speed, step, motion) {
if (t01 < 0 || t01 > 1 || speed <= 0) {
element.scrollTop = xTo;
return;
}
element.scrollTop = xFrom - (xFrom - xTo) * motion(t01);
t01 += speed * step;
setTimeout(function() {
scrollToX(element, xFrom, xTo, t01, speed, step, motion);
}, step);
}
function linearTween(t) {
return t;
}
function easeInQuad(t) {
return t * t;
}
function easeOutQuad(t) {
return -t * (t - 2);
}
function easeInOutQuad(t) {
t /= 0.5;
if (t < 1) return t * t / 2;
t--;
return (t * (t - 2) - 1) / 2;
}
function easeInCuaic(t) {
return t * t * t;
}
function easeOutCuaic(t) {
t--;
return t * t * t + 1;
}
function easeInOutCuaic(t) {
t /= 0.5;
if (t < 1) return t * t * t / 2;
t -= 2;
return (t * t * t + 2) / 2;
}
function easeInQuart(t) {
return t * t * t * t;
}
function easeOutQuart(t) {
t--;
return -(t * t * t * t - 1);
}
function easeInOutQuart(t) {
t /= 0.5;
if (t < 1) return 0.5 * t * t * t * t;
t -= 2;
return -(t * t * t * t - 2) / 2;
}
function easeInQuint(t) {
return t * t * t * t * t;
}
function easeOutQuint(t) {
t--;
return t * t * t * t * t + 1;
}
function easeInOutQuint(t) {
t /= 0.5;
if (t < 1) return t * t * t * t * t / 2;
t -= 2;
return (t * t * t * t * t + 2) / 2;
}
function easeInSine(t) {
return -Mathf.Cos(t / (Mathf.PI / 2)) + 1;
}
function easeOutSine(t) {
return Mathf.Sin(t / (Mathf.PI / 2));
}
function easeInOutSine(t) {
return -(Mathf.Cos(Mathf.PI * t) - 1) / 2;
}
function easeInExpo(t) {
return Mathf.Pow(2, 10 * (t - 1));
}
function easeOutExpo(t) {
return -Mathf.Pow(2, -10 * t) + 1;
}
function easeInOutExpo(t) {
t /= 0.5;
if (t < 1) return Mathf.Pow(2, 10 * (t - 1)) / 2;
t--;
return (-Mathf.Pow(2, -10 * t) + 2) / 2;
}
function easeInCirc(t) {
return -Mathf.Sqrt(1 - t * t) - 1;
}
function easeOutCirc(t) {
t--;
return Mathf.Sqrt(1 - t * t);
}
function easeInOutCirc(t) {
t /= 0.5;
if (t < 1) return -(Mathf.Sqrt(1 - t * t) - 1) / 2;
t -= 2;
return (Mathf.Sqrt(1 - t * t) + 1) / 2;
}
Very long page.Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page.Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page. Very long page.
<button type="button">To the top</button>
Upvotes: 4
Views: 266
Reputation: 1161
So the motion function you happen to pass in your example is easeOutCubic
which is defined as
function easeOutCubic(t) {
t--;
return t * t * t + 1;
}
First, you increment t by speed * step
every time scrollToX
is called. Because both of these variables are constant within the scope of the function, t
will approach 1 at a constant rate. If you decrement t
by 1, it will always yield a negative number greater than -1 since the function works on the interval (0,1)
. As you keep calling scrollToX
then (t-1)^3 + 1
will get a larger but at a slower rate. This is because as t
gets constantly larger, then the absolute value of (t-1)
gets decremented constantly and because (t-1)
is always less than 0, (t-1)^3
will approach 0 at a decreasing rate, and so (t-1)^3 + 1
will approach 1 at the same decreasing rate. This could all be expressed more concisely by the fact that d^2((t-1)^3)/dt^2 < 0
for all t < 1
but decided to give a more drawn out explanation.
You can think of the function motion(t)
as a factor between 0 and 1 that you multiply by the end final distance traveled or (xFrom-xTo)
, to find the total distance traveled at a specific point in time.
Edit (taken from my comments below which I deleted):
t
is a variable whose value is directly proportional to the amount of times scrollToX
has been called, and because scrollToX
gets called at specific time intervals (more or less), that value is a function of time. Now we plug that number into any of the motion functions. Lets now take linearTween
as our example since it just returns the input.
As you increase t
from 0 to .02 to .04, motion(t)
will increase at constant rate. You can multiply motion(t)
, which will yield a number between 0 and 1 (this value will change with time and eventually reach a value of 1) and multiply it by total distance to be traveled, (xFrom-xTo)
, to find a lesser distance traveled for a specific t
. You subtract this lesser-than-final distance traveled from the point of origin, xFrom
, to get the position of the scroll bar at a specific t
.
Visual example:
xTo = 100
|
|
||
||
||
xFrom = 200
The first column of of vertical bars represents total distance to be traveled which in this case equals 100. The second row of vertical bars represents the distance traveled when motion(t) = 0.6 (arbitrarily chosen). If we want to find the position of the top of the scrollbar when motion(t) = 0.6 we have to calculate 200-(100)*0.6, or in general form xFrom - (xFrom - xTo)*motion(t)
.
Upvotes: 2
Reputation: 4876
First of all, let me quote Robert Penners definition of motion
a motion is a numerical change in position over time.
The above can be written as:
pf = p0 + motion(time)
where:
- p0 = initial position
- pf = final position
Motion can only exist when time exists and therefore it's a function of time. Since any position is reached through motion from an initial position we can also say that position is a function of time so
position = f(time)
The above is the typical y = f(x)
2d function, let's assume for a moment that the function is a line and that the motion occurs over 1 second, the function will be plotted as
The slope of the line is given by
m = (pf - p0) / (1 - 0) = pf - p0 (1)
The slope will help us find px
when 0 < t < 1
because the slope is the same for any two points that belong to the line, therefore
m = (px - p0) / (t - 0) = (px - p0) / t (2)
Substituting (1)
in (2)
and finding the value of px
pf - p0 = (px - p0) / t
t * (pf - p0) = px - p0
px = p0 + (pf - p0) * t
Which is exactly to your original equation (the minus sign is factorized by some reason in your equation)
Also note that:
t = 0
then px = p0
t = 1
then px = pf
Now to the value of t
, we know that 0 <= t <= 1
, we can create another function that depends on t
e.g. motion(t)
with the following requirements:
motion(0) = 0
motion(1) = 1
These conditions must hold for the original px
equation to have the correct values, the simplest motion function is the linear or identity function:
const linear = t => t
Another function is the quadratic one:
const quadratic = t => t * t
The position function will be then
px = p0 + (pf - p0) * motion(t)
You'll find a lot of easing functions over the internet, finally, as we know the value of t
will increase proportionally to the time elapsed, given an initial time t0
we can compute the next time tf
with the same definition of motion
tf = t0 + delta(t)
Now if you want to speed up/slow down your animation, you only have to multiply delta(t)
with a number k
, if k > 1
then the animation will happen faster k < 1
the animation will happen slower, therefore:
px = p0 + (pf - p0) * motion(t0)
tf = t0 + k * delta(t)
// the final time will be the initial time for the next iteration
t0 = tf
For example, if you want the animation to happen in 0.5 seconds (twice as fast) k = 2
, if the duration is 2.0 seconds (twice as slow) k = 0.5
, the formula to find the value of k
is then:
k = 1 / duration
Upvotes: 2