Poudro's blog
CTO / Data Scientist / Problem Solver - Consultant

The Bezier Curve and custom animations in CSS3 and Javascript

The Bezier Curve and custom animations in CSS3 and Javascript - poudro.com développeur web freelance

How do you make CSS3 transforms and custom Javascript effects work hand in hand ? You have fun with Bezier curves !

The other day I was trying to handle an effect on several elements at the same time. For a set of reasons, I needed to have one element controlled by javascript (mootools Fx) and other elements controlled by CSS3 transforms (which you can read all about here).

CSS3 transforms are handled through Bezier curves and there is not much you can do about that (as is described here). On the other hand, the mootools Fx effect can be controlled by a custom callback.

So, the first step is figuring out what Bezier parameters you want. The CSS3 property that handles this is ‘transition-timing-function‘. There are five available pre-set values (linear, ease, ease-in, ease-out, ease-in-out) and one that lets you define your custom function as value : cubic-bezier. As its name suggests, this is a Cubic Bezier (the wikipedia page all about about Bezier curves). The already metionned webkit blog post has this to say about the transition :

The transition-timing-function property describes how the animation will proceed over time. Keywords can be used for common functions or the control points for a cubic bezier function can be given for complete control of the transition function. To specify a cubic bezier function, you give an X and Y values for 2 of the control points of the curve. Point P0 is implicitly set to (0,0) and P3 is implicitly set to (1,1). These 4 points are used to compute a cubic bezier curve.

This means that to completely customize the transition as desired, you need to decide what coordinates you’re going to use for P1 and P2. Bezier curves are very common, but I couldn’t really find a good ‘in page’ helper solution. So I decided to build one for myself, what I came up with is below. In the left pane is the speed profile as a Bezier curve and on the right you can activate an animation that uses the corresponding profile.

  Set the animation curve by dragging the white dots
  Test the corresponding animation by clicking in the window

Click me to see animation


In the left window you can see the current Bezier curve applied to the transformation and in the right you can see the effect in action (if your browser supports CSS3 transforms…) You can modify the Bezier curve by dragging the white points around. As you change the curve you can test the corresponding effect in the right window. The blue dot will move to the position of the click with the corresponding transition.

-> A full screen version can be found here.

Once you have those points coordinates, you need to apply the same effect to the mootools transitions. By default, there is no Bezier transition in mootools, so you need to define a custom one. This is made relatively easy through the Fx.Transitions Class provided by mootools which is a wrapper to the callback that you can use in all mootools transitions.

The way this works, relative to the Bezier curve above, the callback takes the x coordinate as a parameter and returns the y coordinate. Bezier curves are parametric, i.e. x and y coordinates are not directly linked, so you need to apply a little mathematics to get what you want (which is very similar to this).

Finally, this is done by the code below which takes the 2 points and returns the Fx.Transitions Object.

   function getBezierTransition(P1, P2)
        bzr = [P1.x, P1.y, P2.x, P2.y],
        bzrM = (function(p){ p.push(3*p[0], 3*p[2]-6*p[0], 1+3*p[0]-3*p[2]); return p; }.bind(this,bzr.slice(0)))(),
        bzrTrans = new Fx.Transition( 
            var t = x,
                nRefinementIterations = 15;

              var t3 = Math.pow(t,3), 
                  t2 = Math.pow(t,2),
                  currentx = t*p[4] + t2*p[5] + t3*p[6],
                  den = (p[4] + 2*t*p[5] + 3*t2*p[6]),
                  currentslope = (den==0)?.1:1.0/den;
              t -= (currentx - x)*(currentslope);
              t = Math.max(0, Math.min(1, t));
            var y = 3* Math.pow((1-t),2) * t * p[1] + 3 * (1-t) * Math.pow(t,2) * p[3] + Math.pow(t,3);  
            return y;
          }, bzrM);

          return bzrTrans;

Once you have this, you can use the returned Object as the transition property of any of your mootools Fx effects.

On a final note, the calculations between the CSS3 and the Javascript transitions are being done independantly in the browser, there is a little jitter between the two, but in the end it’s all for fun anyway… It’s also a possible fallback solution when CSS3 transforms are not supported by the browser, although it seems like this would be overkill unless you really need the transition to be exactly the same.

15 Oct 2011