Jump to content
Search Community

From CSS cubic-bezier to GSAP ease

emmanuelulloa test
Moderator Tag

Go to solution Solved by emmanuelulloa,

Recommended Posts

Hello friends.

Recently while working on a project I needed to make something bounce.

However the regular bounce function was a little too big.

I remember having some css cubic-bezier functions that I used a lot.

Fortunately GSAP accepts eases as functions.  So I asked ChatGPT to create a function that will transform my old css cubic-beziers into GSAP ease functions.

Here is the function code:

function parseCubicBezier(easeString) {
  const [x1, y1, x2, y2] = easeString
    .replace(/cubic-bezier|\(|\)/g, '')
    .split(',')
    .map(parseFloat);

  function cubicBezier(t) {
    const cx = 3.0 * x1;
    const bx = 3.0 * (x2 - x1) - cx;
    const ax = 1.0 - cx - bx;
    const cy = 3.0 * y1;
    const by = 3.0 * (y2 - y1) - cy;
    const ay = 1.0 - cy - by;

    function sampleCurveDerivativeX(t) {
      return (3.0 * ax * t + 2.0 * bx) * t + cx;
    }

    function sampleCurveX(t) {
      return ((ax * t + bx) * t + cx) * t;
    }

    function solveCurveX(x, epsilon) {
      let t0, t1, t2, x2, d2, i;
      for (t2 = x, i = 0; i < 8; i++) {
        x2 = sampleCurveX(t2) - x;
        if (Math.abs(x2) < epsilon) {
          return t2;
        }
        d2 = sampleCurveDerivativeX(t2);
        if (Math.abs(d2) < 1e-6) {
          break;
        }
        t2 = t2 - x2 / d2;
      }
      t0 = 0.0;
      t1 = 1.0;
      t2 = x;
      if (t2 < t0) {
        return t0;
      }
      if (t2 > t1) {
        return t1;
      }
      while (t0 < t1) {
        x2 = sampleCurveX(t2);
        if (Math.abs(x2 - x) < epsilon) {
          return t2;
        }
        if (x > x2) {
          t0 = t2;
        } else {
          t1 = t2;
        }
        t2 = (t1 - t0) * 0.5 + t0;
      }
      return t2; // Failure.
    }

    function solve(x, epsilon) {
      return sampleCurveY(solveCurveX(x, epsilon));
    }

    function sampleCurveY(t) {
      return ((ay * t + by) * t + cy) * t;
    }

    return solve(t, 1 / 200);
  }

  return cubicBezier;
}

// Example usage
const easeFunction = parseCubicBezier('cubic-bezier(.17,.67,.83,.67)');
const easedPosition = easeFunction(0.5);
console.log(easedPosition);

And here is my collection of css cubic-beziers extracted from a SCSS library I used in another project:

  "easeInSine": cubic-bezier(0.12, 0, 0.39, 0),
  "easeOutSine": cubic-bezier(0.61, 1, 0.88, 1),
  "easeInOutSine": cubic-bezier(0.37, 0, 0.63, 1),
  "easeInQuad": cubic-bezier(0.11, 0, 0.5, 0),
  "easeOutQuad": cubic-bezier(0.5, 1, 0.89, 1),
  "easeInOutQuad": cubic-bezier(0.45, 0, 0.55, 1),
  "easeInCubic": cubic-bezier(0.32, 0, 0.67, 0),
  "easeOutCubic": cubic-bezier(0.33, 1, 0.68, 1),
  "easeInOutCubic": cubic-bezier(0.65, 0, 0.35, 1),
  "easeInQuart": cubic-bezier(0.5, 0, 0.75, 0),
  "easeOutQuart": cubic-bezier(0.25, 1, 0.5, 1),
  "easeInOutQuart": cubic-bezier(0.76, 0, 0.24, 1),
  "easeInQuint": cubic-bezier(0.64, 0, 0.78, 0),
  "easeOutQuint": cubic-bezier(0.22, 1, 0.36, 1),
  "easeInOutQuint": cubic-bezier(0.83, 0, 0.17, 1),
  "easeInExpo": cubic-bezier(0.7, 0, 0.84, 0),
  "easeOutExpo": cubic-bezier(0.16, 1, 0.3, 1),
  "easeInOutExpo": cubic-bezier(0.87, 0, 0.13, 1),
  "easeInCirc": cubic-bezier(0.55, 0, 1, 0.45),
  "easeOutCirc": cubic-bezier(0, 0.55, 0.45, 1),
  "easeInOutCirc": cubic-bezier(0.85, 0, 0.15, 1),
  "easeInBack": cubic-bezier(0.36, 0, 0.66, -0.56),
  "easeOutBack": cubic-bezier(0.34, 1.56, 0.64, 1),
  "easeInOutBack": cubic-bezier(0.68, -0.6, 0.32, 1.6),
  "sine-ease-in": cubic-bezier(0.47, 0, 0.745, 0.715),
  "sine-ease-out": cubic-bezier(0.39, 0.575, 0.565, 1),
  "sine-ease-in-out": cubic-bezier(0.39, 0.575, 0.565, 1),
  "quad-ease-out": cubic-bezier(0.25, 0.46, 0.45, 0.94),
  "quad-ease-in": cubic-bezier(0.55, 0.09, 0.68, 0.53),
  "quad-ease-in-out": cubic-bezier(0.455, 0.03, 0.515, 0.955),
  "cubic-ease-in": cubic-bezier(0.55, 0.06, 0.68, 0.19),
  "cubic-ease-out": cubic-bezier(0.22, 0.61, 0.36, 1),
  "cubic-ease-in-out": cubic-bezier(0.65, 0.05, 0.36, 1),
  "quart-ease-in": cubic-bezier(0.895, 0.03, 0.685, 0.22),
  "quart-ease-out": cubic-bezier(0.165, 0.84, 0.44, 1),
  "quart-ease-in-out": cubic-bezier(0.77, 0, 0.175, 1),
  "quint-ease-in": cubic-bezier(0.755, 0.05, 0.855, 0.06),
  "quint-ease-out": cubic-bezier(0.23, 1, 0.32, 1),
  "quint-ease-in-out": cubic-bezier(0.86, 0, 0.07, 1),
  "circ-ease-in": cubic-bezier(0.6, 0.04, 0.98, 0.335),
  "circ-ease-out": cubic-bezier(0.075, 0.82, 0.165, 1),
  "circ-ease-in-out": cubic-bezier(0.785, 0.135, 0.15, 0.86),
  "expo-ease-out": cubic-bezier(0.19, 1, 0.22, 1),
  "expo-ease-in": cubic-bezier(0.6, 0.04, 0.98, 0.335),
  "expo-ease-in-out": cubic-bezier(0.785, 0.135, 0.15, 0.86),
  "back-in": cubic-bezier(0.6, -0.28, 0.74, 0.05),
  "back-out": cubic-bezier(0.18, 0.89, 0.32, 1.28),
  "back-in-out": cubic-bezier(0.68, -0.55, 0.265, 1.55),
  "bounce-in": cubic-bezier(0.24, -0.44, 0.17, 0.27),
  "bounce-out": cubic-bezier(0.64, 0.57, 0.67, 1.53),
  "bounce-in-out": cubic-bezier(0.76, -0.245, 0.24, 1.245),
  "snap-out": cubic-bezier(0, 1, 0.5, 1),
  "snap-in": cubic-bezier(0.06, 0.98, 0.19, 0.99),
  "smooth": cubic-bezier(0.365, 0.005, 0.355, 1),
  "sine": cubic-bezier(0.39, 0.58, 0.57, 1),
  "quad": cubic-bezier(0.25, 0.46, 0.45, 0.94),
  "cubic": cubic-bezier(0.22, 0.61, 0.36, 1),
  "circ": cubic-bezier(0.075, 0.82, 0.165, 1),
  "expo": cubic-bezier(0.19, 1, 0.22, 1),
  "bounce": cubic-bezier(0.65, 1.95, 0.03, 0.32),
  "spring": cubic-bezier(0.25, 2, 0.75, 0.5),
  "elastic": cubic-bezier(0.47, 2.02, 0.31, -0.36),
  "swing": cubic-bezier(0.175, 0.885, 0.32, 1.275),
  "overshoot": cubic-bezier(0.41, 0.41, 0.35, 1.2),
  "anticipate": cubic-bezier(0.5, 0, 0.8, 0.15),
  "hesitate": cubic-bezier(0.2, 1, 0.9, -0.5),
  "hesitate2": cubic-bezier(0.5, 1, 0.5, -0.25),
  "snap": cubic-bezier(0.755, 0.05, 0.855, 0.06),
  "sharp": cubic-bezier(0.4, 0, 0.6, 1),
  "acceleration": cubic-bezier(0.4, 0, 1, 1),
  "deceleration": cubic-bezier(0, 0, 0.2, 1),
  "standard": cubic-bezier(0.4, 0, 0.2, 1),
  "paused": cubic-bezier(0, 1, 1, 0),
  "circ-in-out": cubic-bezier(0.88, 0.14, 0.12, 0.86),
  "cubic-in-out": cubic-bezier(0.66, 0, 0.34, 1),
  "ease-in-out2": cubic-bezier(0.42, 0, 0.58, 1),
  "expo-in-out": cubic-bezier(0.9, 0, 0.1, 1),
  "quad-in-out": cubic-bezier(0.48, 0.04, 0.52, 0.96),
  "quart-in-out": cubic-bezier(0.76, 0, 0.24, 1),
  "quint-in-out": cubic-bezier(0.84, 0, 0.16, 1),
  "fast-slow": cubic-bezier(0, 0.45, 0.45, 1),
  "fast-fast-slow": cubic-bezier(0, 0.9, 0.45, 1),
  "fast-slow-slow": cubic-bezier(0, 0.45, 0.1, 1),
  "slow-fast": cubic-bezier(0.45, 0, 1, 0.45),
  "slow-fast-fast": cubic-bezier(0.45, 0, 1, 0.15),
  "slow-slow-fast": cubic-bezier(0.9, 0, 1, 0.45),
  "ant-s1": cubic-bezier(1, -0.51, 0, 1.51),
  "ant-s2": cubic-bezier(0.99, -2.18, 0.01, 3.18),
  "ant-q1": cubic-bezier(0.5, -1, 0.5, 2),
  "ant-q2": cubic-bezier(0.5, -1.5, 0.5, 2.5),
  "smooth-operator": cubic-bezier(0.64, 0.03, 0.07, 0.97),
  "endless-bummer": cubic-bezier(0.99, 0.03, 0.42, 0.97),
  "get-around": cubic-bezier(1, 0.07, 0.11, 1),
  "unknown-soldier": cubic-bezier(0.33, 0.02, 0.11, 1),
  "spanish-caravan": cubic-bezier(0.76, 0, 0.43, 0.94),
  "five-to-one": cubic-bezier(0.32, 0.05, 0.27, 1),
  "wild-in-the-streets": cubic-bezier(0.5, 1, 0.35, 1),
  "daniel": cubic-bezier(0, 0.1, 0.53, 1.34),
  "cocodrile-tears": cubic-bezier(0.47, 1.35, 0.53, 1.33),
  "crystal-ship": cubic-bezier(0.99, -0.55, 0.73, 1),
  "tencc": cubic-bezier(0.14, -0.14, 0, 1),
  "bishop": cubic-bezier(1, -0.66, 0.42, 0.97),
  "scarborough-fair": cubic-bezier(1, 0.06, 0.73, 0.98),
  "tulsa-time": cubic-bezier(0.19, 2.51, 0.43, 1),
  "norwegian-skog": cubic-bezier(0.25, 0.51, 0.35, 0.95),
  "weird-fishes": cubic-bezier(0.88, 0.04, 0.88, 0.95),
  "brave-strangers": cubic-bezier(0, 0.01, 0, 1),
  "stranger-than-diction": cubic-bezier(1, 0, 0.74, 1),
  "the-gambler": cubic-bezier(0.47, -0.48, 0, 1.03),
  "cracklin-rose": cubic-bezier(0.05, 0.03, 0, 1.16),
  "ease-10": cubic-bezier(0.1, 0, 0.9, 1),
  "ease-20": cubic-bezier(0.2, 0, 0.8, 1),
  "ease-30": cubic-bezier(0.3, 0, 0.7, 1),
  "ease-40": cubic-bezier(0.4, 0, 0.6, 1),
  "ease-50": cubic-bezier(0.5, 0, 0.5, 1),
  "ease-60": cubic-bezier(0.6, 0, 0.4, 1),
  "ease-70": cubic-bezier(0.7, 0, 0.3, 1),
  "ease-80": cubic-bezier(0.8, 0, 0.2, 1),
  "ease-90": cubic-bezier(0.9, 0, 0.1, 1),
  "ease-100": cubic-bezier(1, 0, 0, 1),
  "ease-0-10": cubic-bezier(0, 0, 0.1, 1),
  "ease-0-20": cubic-bezier(0, 0, 0.2, 1),
  "ease-0-30": cubic-bezier(0, 0, 0.3, 1),
  "ease-0-40": cubic-bezier(0, 0, 0.4, 1),
  "ease-0-50": cubic-bezier(0, 0, 0.5, 1),
  "ease-0-60": cubic-bezier(0, 0, 0.6, 1),
  "ease-0-70": cubic-bezier(0, 0, 0.7, 1),
  "ease-0-80": cubic-bezier(0, 0, 0.8, 1),
  "ease-0-90": cubic-bezier(0, 0, 0.9, 1),
  "ease-50-0": cubic-bezier(0.5, 0, 0, 1),
  "ease-50-10": cubic-bezier(0.5, 0, 0.1, 1),
  "ease-50-20": cubic-bezier(0.5, 0, 0.2, 1),
  "ease-50-30": cubic-bezier(0.5, 0, 0.3, 1),
  "ease-50-40": cubic-bezier(0.5, 0, 0.4, 1),
  "ease-50-50": cubic-bezier(0.5, 0, 0.5, 1),
  "ease-50-60": cubic-bezier(0.5, 0, 0.6, 1),
  "ease-50-70": cubic-bezier(0.5, 0, 0.7, 1),
  "ease-50-80": cubic-bezier(0.5, 0, 0.8, 1),
  "ease-50-90": cubic-bezier(0.5, 0, 0.9, 1),
  "ease-50-100": cubic-bezier(0.5, 0, 1, 1),
  "boing": cubic-bezier(0.35, 2, 0.1, 0.2),
  "boingPast": cubic-bezier(0.88, 2, 0.48, 0.71),
  "swoosh": cubic-bezier(0.63, -0.28, 0.13, 1.55),
  "gottaDash": cubic-bezier(0.9, -1.4, 0.61, 1.61),
  "slowMo": cubic-bezier(0, 1.35, 1, -0.35),
  "slowInOut": cubic-bezier(0.36, -0.14, 0.29, 1.21),
  "smallOver": cubic-bezier(0.41, 0.41, 0.35, 1.1),
  "superEase": cubic-bezier(0.605, 0.165, 0.045, 0.945),
  "strongOut": cubic-bezier(0, 0, 0.255, 0.995),
  "strongIn": cubic-bezier(0.975, 0.015, 1, 1),
  "moon-shine": cubic-bezier(.19, 1.78, .73, 1.35),
  "tailwind-in": cubic-bezier(.8, 0, 1, 1),
  "tailwind-out": cubic-bezier(0, 0, .2, 1),
  "tailwind-in-out": cubic-bezier(.4, 0, .6, 1),
  "tiny-bounce": cubic-bezier(.96, 1.34, .6, .89),
  "small-bounce": cubic-bezier(.99, 1.6, 0, .72),
  "easeout": cubic-bezier(0.5, 0.5, 0, 1),
  "jump": cubic-bezier(.86, 0, .69, 1.57)

Then you can use it like this:

.to("#element", {xPercent: 50, ease: parseCubicBezier('cubic-bezier(.96, 1.34, .6, .89)') }, "INTRO")

I think that some of you might find it useful so I like to share it with the community.

Link to comment
Share on other sites

1 hour ago, emmanuelulloa said:

However the regular bounce function was a little too big.

What do you mean? You can't actually get a bounce ease with one cubic bezier.

 

And what's "too big"? The GSAP "bounce" ease? That's in the core, so there's no extra kb, plus it's much less code than your helper function there. Could you elaborate a bit on what exactly you're trying to solve here? 

 

CustomEase solves all of this for you, by the way, plus it gives WAY more flexibility. From the docs

Quote

CustomEase also recognizes standard cubic-bezier() strings containing four numbers, like those you can get from cubic-bezier.com. For example, ".17,.67,.83,.67". Either paste that into the orange text area in the bottom of the Ease Visualizer or feed it directly into the CustomEase.create() method, like CustomEase.create("easeName", ".17,.67,.83,.67");

 

So your code could be even shorter: 

// OLD
.to("#element", {xPercent: 50, ease: parseCubicBezier('cubic-bezier(.96, 1.34, .6, .89)') }, "INTRO")

// NEW
.to("#element", {xPercent: 50, ease: CustomEase.create("", ".96, 1.34, .6, .89") }, "INTRO")

And the way CustomEase works is much, much more optimized than what ChatGPT provided you there. So performance-wise, CustomEase is superior. And again, it allows you to get a true bounce with as many bezier anchors as you want. With the solution you posted, you're limited to two anchor points which severely limits the types of eases you can get. 

 

https://greensock.com/docs/v3/Eases/CustomEase

 

Perhaps I'm misunderstanding the problem you're trying to solve, though? 

Link to comment
Share on other sites

  • Solution
Quote

However the regular bounce function was a little too big.

I meant the bouncing height is too big.  It just needed a tiny bit of bouncing.

Quote

CustomEase also recognizes standard cubic-bezier() strings containing four numbers, like those you can get from cubic-bezier.com. For example, ".17,.67,.83,.67". Either paste that into the orange text area in the bottom of the Ease Visualizer or feed it directly into the CustomEase.create() method, like CustomEase.create("easeName", ".17,.67,.83,.67");

🤩 I did not know!

This is what I mean in regards to bouncing in cubic-bezier:
cubic-bezier(.96, 1.34, .6, .89) gets transformed into:
Tiny Bounce

cubic-bezier(0.88, 2, 0.48, 0.71) gets transformed into:

Boing Past 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...