meduzen / easings

Easings (cubic-bezier timing functions) as custom properties and SCSS variables.
Do What The F*ck You Want To Public License
12 stars 0 forks source link

(ideas) How to better write easings using CSS or SCSS? #1

Open meduzen opened 4 years ago

meduzen commented 4 years ago

How to better write easings using CSS or SCSS?

(Please note the question is not “How to write better timing functions”, but “How to better write [them]”).

I want to improve the way I write CSS cubic-bezier() timing functions. I’d like to request ideas and opinions 🙏, and share a few ideas. What does (or doesn’t) work for you, and why?

What I do now

What I’ve always done is using a variable coming from the SCSS library Bourbon:

.my-class {
  transition: transform .3s $ease-in-out-quad;
 }

which outputs:

.my-class {
  transition: transform .3s cubic-bezier(0.55, 0.085, 0.68, 0.53);
 }

It’s rather straightforward, but I’d like to replace this by using something else, more modern/powerful/straightforward.

It can use SCSS, PostCSS, or both, but it has to be framework agnostic.

Three types of solutions

Existing ideas:

  1. Same as SCSS variables, but with custom props declared at :root level.
  2. A SCSS ease() function requiring to write transition: transform .3s ease(easeInOutQuad);.
  3. postcss-easings, a PostCSS plugin allowing to write transition: transform .3s ease-in-out-quad; to transform it using the matching cubic-bezier value (camelCase also works: easeInOutQuad).

So, three types of solutions: custom properties, a SCSS function, and a PostCSS plugin.

There are various improvements that could be made to each. One of them is getting rid of the ease* prefix in order to shorten the syntax:

.my-class {
  transition: transform .3s $ease-in-out-quad; // SCSS variable
  transition: transform .3s $in-out-quad; // shortened SCSS variable

  transition: transform .3s var(--ease-in-out-quad); // custom prop
  transition: transform .3s var(--in-out-quad); // custom prop shortened
  transition: transform .3s ease-in-out-quad; // postcss-easings
  transition: transform .3s in-out-quad; // postcss-easings shortened
 }

Maybe one of the three type of solutions could fit as a good basis. But maybe shortening the syntax is only one part of the possible improvements and considerations that can be tackled.

More

(done ☑️) Reverse easings

See Reversing an easing curve. It’s powerful and important. I have not seen reversed easings in a library, yet, and I think it could easily be integrated in whatever solution.

(done ☑️) Pulling the package

Supposing a SCSS package, I’d probably want:

@import '~awesome-easings';

This single @import would output all the easings available, this way:

$in-out-quad: var(--in-out-quad);
$in-quad: var(--in-quad);
/* more easings… */

:root {
  --in-out-quad: cubic-bezier(.250, .460, .450, .940);
  --in-quad: cubic-bezier(.550, .085, .680, .530);
  /* more props… */
}

Alternatively, a partial import could be a solution:

@import '~awesome-easings/src/easings/in-out-quad';

(done ☑️) Custom props and backward compatibility

A flag could exclude/include legacy output. Example with SCSS:

$awesome-easings-legacy: true;
@import '~awesome-easings';

.my-class {
  transition: transform .3s $in-out-quad;
}

With $awesome-easing-legacy, the CSS output should be:

.my-class {
  transition: transform .3s cubic-bezier(.250, .460, .450, .940);
}

(Alternatively, postcss-preset-env is a better solution.)

Naming from the JS world

GSAP names easings differently:

But the following names are shared: sine, expo, circ, back.

That’s interesting because PowerX are named according to the math behind their curves (minus 1), and such a naming is very convenient to shape the acceleration/deceleration power of their curves.

So maybe it could be a good idea to alias quad to power1, for example.

Splitted easings

At first I was thinking about taking advantage of GSAP naming to make a very short syntax. For example:

.my-class {
  transition: transform .3s e(4, io); // Power4.inOut, or $in-out-quint
}

But anything in that direction will likely cause readability issue.

Instead, a function could fit another purpose, like splitting an easing curve into several curves at specified breakpoints. This is inspired by split ease, a JavaScript function inserting a linear gap between the acceleration and the deceleration of an easing curve. So maybe this could give an idea for a useful function.

It could look like this:

/* Create 3 easing curves that are segments of $in-out-quad ranging:
   - from 0 to 33% of the curve;
   - from 33% to 80% of the curve;
   - from 80% to 100% of the curve.
*/
$splitted-easing: ease-split($in-out-quad, 0.33, 0.8);

// Apply each curve to its own element
.my-class {
  > :nth-child(1) {
    transition: transform .33s ease($splitted-easing, 1));
  }

  > :nth-child(2) {
    transition: transform .47s ease($splitted-easing, 2) .33s;
  }

  > :nth-child(3) {
    transition: transform .2s ease($splitted-easing, 3) .8s;
  }
}

Other idea: allowing easings to receive a cx (article).

CSS utility classes

<div class="in-out-quad">

I don’t have any interest or need, but who knows… Does someone have a good use case for this?


Of course, all of these shouldn’t prevent you to directly play with your custom easing curves using the dev tools.

josephrexme commented 4 years ago

What I currently do is set the values that go in cubic-bezier as custom properties at root level and then call cubic-bezier on them.

:root{
  --app-easing-1: .5, 0, .5, 1;
  --app-easing-2: 0.55, 0.085, 0.68, 0.53;
}

.some-time-later{
  animation: rollOver 1s cubic-bezier(var(--app-easing-1));
}

.some-transition-later{
  transition: transform .5s cubic-bezier(var(--app-easing-2));
}
meduzen commented 4 years ago

Interesting! By proxying the name of an easing variable, this could also lead to the use of a SCSS function to keep the syntax short:

:root{
  --ease-in-quad: cubic-bezier(.55, .085, .68, .53);
  --app-easing-2: var(--ease-in-quad);
}
.my-class {
  transition: transform .5s cb(2); // supposing all easings are prefixed `app-easing-`
}

could generate:

.my-class {
  transition: transform .5s cubic-bezier(var(--app-easing-2));
}