2
votes

I'm experimenting in Javascript with animating various elements using a cubic-bezier timing function.

(I know this is usually better done with CSS3, or a Javascript animation library. I'm simply using Javascript here to understand how bezier functions work, and teach myself about them.)

So, I get the basic concept, and I'm using a simple bezier curve library written by Stephen McKamey, which is a great Javascript port of the Webkit implementation.

I'm having trouble understanding how I can actually use this function to control an animation in Javascript. So, starting with something very simple: a basic black square that I can animate by moving it to the right, by incrementing its style.left property:

CSS:

.parent {
    position: relative;
}   

.foo {
    border: 1px solid #000000;
    background-color: black;
    height: 200px;
    width: 200px;
    position: absolute;
}

HTML:

<div class = "parent">
    <div class = "foo" id = "target"></div>
</div>

Okay, so, given a cubic bezier function "bezier", defined as follows:

   function bezier(p1x, p1y, p2x, p2y, x, duration) { ... }

Where p1x, p1y, p2x and p2y are the curve control points (between 0 and 1.0), x is the value of the x coordinate, and duration is a duration in milliseconds. The function returns the corresponding y coordinate. I'm trying to simply animate this black box by moving it 400px to the right.

For my first attempt, I use the standard "ease" bezier values, which CSS3 uses, so our ease bezier function could be written as:

function ease(x, duration) {
    return function() {
        Bezier.cubicBezier(0.25, 0.1, 0.25, 1.0, x, duration);
    }
}

So this should give us a slow start, then move fast, then end slowly.

Okay, so I assume the basic way to implement this is to use window.setInterval, and then for each interval, call the bezier function with a new value for x, and then apply the result somehow to the property we want to animate.

The thing is, I'm not sure what my "x" value is here. I assume that in this situation, x is actually the time, and y is the delta between the old position and new position (distance to move), but I'm not really sure. I'm probably wrong.

Anyway, plugging this all in, I'd write a function like:

var startPos = 0;
var endPos = 400; // pixels
var duration = 400; // milliseconds
var millisecondsPerInterval = 10;

var target = document.getElementById("target");
var t = 0;
var pos = 0;

var bezierFunction = Bezier.cubicBezier;

var interval = window.setInterval(
    function() {
        pos = pos + bezierFunction(0.25, 0.1, 0.25, 1.0, t / 1000, duration); // "ease" values
        target.style.left = (pos * millisecondsPerInterval) + "px";
        t += millisecondsPerInterval;
        if (t === duration) { window.clearInterval(interval); }
    },
    millisecondsPerInterval
);

This seems to work - the box slowly begins moving and then speeds up. But then it just stops abruptly, rather than easing out. So I'm probably not applying this function correctly. I'm not even certain if "x" is supposed to be my time value here, and "y" is supposed to be the position delta (distance to move), but that seems the only way to apply this that makes any sense.

So, am I doing something wrong here? What is the correct way to apply a cubic bezier function to a property we want to animate using Javascript?

1

1 Answers

-2
votes

If you use JQuery, it may make the process simpler.

Based on an answer to a similar question (https://stackoverflow.com/a/6824695/363099), you could extend jQuery easing in order to add your custom easing function:

According to the jQuery 1.6.2 source, the meaning of the easing function is as follows. The function is called at various points in time during the animation. At the instants it is called,

  • x and t both say what the time is now, relative to the start of the animation. x is expressed as a floating point number in the range [0,1], where 0 is the start and 1 is the end. t is expressed in milliseconds since the start of the animation.

  • d is the duration of the animation, as specified in the animate call, in milliseconds.

  • b=0 and c=1.

So here is how it could work for your code:

  $.extend(jQuery.easing,{bezier:   function(x,t,b,c,d) {

        return (
          x <= 0 ? 0 :
          x >= 1 ? 1 :
          bezierFunction(0.25, 0.1, 0.25, 1.0, x, d)
            );   
     } });

Then you can just use JQuery animation function:

$('#target').animate({right:'400px'},400,'bezier');