0

I have one CSS transition of 2 seconds from 0 to 200px at a timing of cubic-bezier(0.4, 0, 0.2, 1):

transition: transform 2s cubic-bezier(0.4, 0, 0.2, 1);

I want to decompose this animation into 2 equal parts:

Part 1: transform-duration 1s from 0 to 100px
Part 2: transform-duration 1s from 100px to 200px, will start after the first one ends

The tricky part is to calculate the right timing fro each one. According to this post: Create easy function 40% off set and this: Extrapolate split cubic-bezier to 1,1 I manage to split my initial Bezier curve and normalize coordinates to (0,0) - (1,1).

Here is my code:

//    Single Animation, 2s from 0 to 200px
var d1 = document.querySelector('.d1')
d1.style.transform = 'translateX(200px)';

//    Composed Animation, Part 1, 1s from 0 to 100px
var d2 = document.querySelector('.d2');

//    Get first part bezier
//    cubic-bezier(0.4, 0, 0.2, 1)
var x = [0, 0.4, 0.2, 1]
var y = [0, 0,   1,   1]

function splitCubicBezier(options) {
  var z = options.z,
      cz = z-1,
      z2 = z*z,
      cz2 = cz*cz,
      z3 = z2*z,
      cz3 = cz2*cz,
      x = options.x,
      y = options.y;

  var left = [
    x[0],
    y[0],
    z*x[1] - cz*x[0], 
    z*y[1] - cz*y[0], 
    z2*x[2] - 2*z*cz*x[1] + cz2*x[0],
    z2*y[2] - 2*z*cz*y[1] + cz2*y[0],
    z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
    z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0]];

  var right = [
    z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
    z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0],
                    z2*x[3] - 2*z*cz*x[2] + cz2*x[1],
                    z2*y[3] - 2*z*cz*y[2] + cz2*y[1],
                                    z*x[3] - cz*x[2], 
                                    z*y[3] - cz*y[2], 
                                                x[3],
                                                y[3]];

  if (options.fitUnitSquare) {
    return {
      left: left.map(function(el, i) {
        if (i % 2 == 0) {

          var Xmin = left[0];
          var Xmax = left[6]; //should be 1
          var Sx = 1 / (Xmax - Xmin);
          return (el - Xmin) * Sx;
        } else {

          var Ymin = left[1];
          var Ymax = left[7]; //should be 1
          var Sy = 1 / (Ymax - Ymin);
          return (el - Ymin) * Sy;
        }
      }),
      right: right.map(function(el, i) {
        if (i % 2 == 0) {
          //xval
          var Xmin = right[0]; //should be 0
          var Xmax = right[6];
          var Sx = 1 / (Xmax - Xmin);
          return (el - Xmin) * Sx;
        } else {
          //yval
          var Ymin = right[1]; //should be 0
          var Ymax = right[7];
          var Sy = 1 / (Ymax - Ymin);
          return (el - Ymin) * Sy;
        }
      })
    }
  } else {
   return { left: left, right: right};
  }
}

var result = splitCubicBezier({
  z: .5, // Split at half
  x: x,
  y: y,
  fitUnitSquare: true // coordinate normalization
});

console.log(result)

d2.addEventListener("transitionend", newTiming, false);

d2.style['transition-timing-function'] = 'cubic-bezier(' + result.left[2] + ',' + result.left[3] + ',' + result.left[4] + ',' + result.left[5] + ')';
d2.style.transform = 'translateX(100px)';

function newTiming(){
    d2.textContent = 'Composed Animation, Part 2'
    d2.style['transition-timing-function'] = 'cubic-bezier(' + result.right[2] + ',' + result.right[3] + ',' + result.right[4] + ',' + result.right[5] + ')';
    d2.style.transform = 'translateX(200px)';
}

http://jsfiddle.net/3hs4ehom/1/

Obviously this do not looks right. The Bezier curve seems splited right http://jsfiddle.net/DfpLk/3/. Am I missing something? Thanks.

Community
  • 1
  • 1
Mircea
  • 11,373
  • 26
  • 64
  • 95
  • You can't just pick `t=0.5` and trust it'll get the point in the "middle" of the function, because it's a parametric function. For your curve the `t=0.5` point is actually the coordinate (0.35,0.5), which is nowhere near the middle with respect to `x` (on the range 0-200px, it'd be at 70px, not 100px!). First, find out which `t` value(s) will yield x=0.5, then take it from there. – Mike 'Pomax' Kamermans Oct 28 '14 at 16:28
  • I am trying to split at t=0.5, not sure that I understand the rest. (0.35,0.5) is the result of the initial split. Isn't this correct? – Mircea Oct 28 '14 at 16:32
  • @Mike'Pomax'Kamermans This will probably give more details on what I am trying to achieve: http://jsfiddle.net/3hs4ehom/3/, fallback to transitions for element.animate() available only in Chrome at this time – Mircea Oct 28 '14 at 16:57
  • 1
    reread your posts. It states (at least in the code at the top, in your comments for that code) that your first curve runs from 0-100px and then second runs from 100-200px. You can't do that by just picking t=0.5 *and* have the transition stay visually the same. You need to pick the splitting `t` value for which x=100 (which is nowhere near 0.5; it's almost 0.7) – Mike 'Pomax' Kamermans Oct 28 '14 at 19:35
  • Thank you, it makes sense now. Is there any formula I could use for getting t at x? – Mircea Oct 28 '14 at 20:41
  • there is; you're solving the equation `Bx(t)=100`. This can be done iteratively (using newton-raphson, for instance) or directly using [Cardano's algorithm](http://jsbin.com/mutaracihafi/1/edit?html,js,output) - http://stackoverflow.com/a/26386793/740553 – Mike 'Pomax' Kamermans Oct 28 '14 at 23:27

0 Answers0