-
-
Save gre/1650294 to your computer and use it in GitHub Desktop.
/* | |
* This work is free. You can redistribute it and/or modify it under the | |
* terms of the Do What The Fuck You Want To Public License, Version 2, | |
* as published by Sam Hocevar. See the COPYING file for more details. | |
*/ | |
/* | |
* Easing Functions - inspired from http://gizma.com/easing/ | |
* only considering the t value for the range [0, 1] => [0, 1] | |
*/ | |
EasingFunctions = { | |
// no easing, no acceleration | |
linear: t => t, | |
// accelerating from zero velocity | |
easeInQuad: t => t*t, | |
// decelerating to zero velocity | |
easeOutQuad: t => t*(2-t), | |
// acceleration until halfway, then deceleration | |
easeInOutQuad: t => t<.5 ? 2*t*t : -1+(4-2*t)*t, | |
// accelerating from zero velocity | |
easeInCubic: t => t*t*t, | |
// decelerating to zero velocity | |
easeOutCubic: t => (--t)*t*t+1, | |
// acceleration until halfway, then deceleration | |
easeInOutCubic: t => t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1, | |
// accelerating from zero velocity | |
easeInQuart: t => t*t*t*t, | |
// decelerating to zero velocity | |
easeOutQuart: t => 1-(--t)*t*t*t, | |
// acceleration until halfway, then deceleration | |
easeInOutQuart: t => t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t, | |
// accelerating from zero velocity | |
easeInQuint: t => t*t*t*t*t, | |
// decelerating to zero velocity | |
easeOutQuint: t => 1+(--t)*t*t*t*t, | |
// acceleration until halfway, then deceleration | |
easeInOutQuint: t => t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t | |
} |
YOU ARE AWESOME !!!
👍🎊🔥💯💣
Also useful for easing is the minimum jerk motion of the 'smootherStep' function:
easeInOutSmoother: function(t) { var ts = t * t, tc = ts * t; return 6*tc*ts - 15*ts*ts + 10*tc },
Example: https://jsfiddle.net/intrinsica/9ryqet30
CSS approximation: cubic-bezier(.49,0,.51,1)
I needed a cubic bezier function to recreate https://cubic-bezier.com/ easing, so I found an article about bezier curves on a canvas and adjusted the code to be a simple timing method.
private easeCubicBezier(t, p1X, p1Y, p2X, p2Y) { return 3 * t * Math.pow(1 - t, 2) * p1X + 3 * t * t * (1 - t) * p2X + t * t * t; }
p1Y and p2Y are not used but I wanted to reflect the exact parameters the css timing function has.
source: http://www.independent-software.com/determining-coordinates-on-a-html-canvas-bezier-curve.html
Do you happen to have a bezier curve function that uses all 4 values?
@Rkokie I've read the article and came up with a function
function CubicBezier(cp1x, cp1y, cp2x, cp2y) {
let BesierEasingFunction
return BesierEasingFunction = t => {
let x = 3 * t * Math.pow(1 - t, 2) * cp1x + 3 * t * t * (1 - t) * cp2x,
y = 3 * t * Math.pow(1 - t, 2) * cp1y + 3 * t * t * (1 - t) * cp2y
return t === 0 || t === 1 ? t : -Math.atan2(x, y) + 0.5*Math.PI;
}
}
BUT it doesn't do any interpolation, it just throws some values from time to time, was wondering if you have something that works as an easing function.
@thednp
If you check the article I've posted you'll find the "getBezierXY" method which will use all values, it does require start and end coordinates though. For the timing when easing it appeared making use of all these other parameters didn't make any difference.
I replaced sx,sy, with 0 and ex,ey with 1 and I get something moving, but they're not correct.
I see you're using the "getBezierAngle" function, which doesn't calculate the position in the bezier curve but the angle the curve is at, at a certain point in the curve.
I used the other function as well, I still don't get the animation to complete, it's like half the distance and wrong coordinates.
I'm curious what you're trying to do that isn't fulfilled with the function I posted. Could you whip up an example in codepen or jsfiddle?
As you can see, I'm looking for a simple and working easing function for cubic bezier. Something like tween.js could use such.
@Rkokie
To tell you the truth I'm trying to fix some problem with @gre 's script, join us here.
Not sure how well it works, but try this (found in Ocanvas):
cubicBezier: function (x1, y1, x2, y2, time) {
// Inspired by Don Lancaster's two articles
// http://www.tinaja.com/glib/cubemath.pdf
// http://www.tinaja.com/text/bezmath.html
// Set start and end point
var x0 = 0,
y0 = 0,
x3 = 1,
y3 = 1,
// Convert the coordinates to equation space
A = x3 - 3*x2 + 3*x1 - x0,
B = 3*x2 - 6*x1 + 3*x0,
C = 3*x1 - 3*x0,
D = x0,
E = y3 - 3*y2 + 3*y1 - y0,
F = 3*y2 - 6*y1 + 3*y0,
G = 3*y1 - 3*y0,
H = y0,
// Variables for the loop below
t = time,
iterations = 5,
i, slope, x, y;
// Loop through a few times to get a more accurate time value, according to the Newton-Raphson method
// http://en.wikipedia.org/wiki/Newton's_method
for (i = 0; i < iterations; i++) {
// The curve's x equation for the current time value
x = A* t*t*t + B*t*t + C*t + D;
// The slope we want is the inverse of the derivate of x
slope = 1 / (3*A*t*t + 2*B*t + C);
// Get the next estimated time value, which will be more accurate than the one before
t -= (x - time) * slope;
t = t > 1 ? 1 : (t < 0 ? 0 : t);
}
// Find the y value through the curve's y equation, with the now more accurate time value
y = Math.abs(E*t*t*t + F*t*t + G*t * H);
return y;
}
The time parameter varies from 0 through 1, returning the corresponding y value.
@jwdunn1 nop, but I thank you for the code.
The UnitBezier function in this CodePen tracks somewhat closely to the CSS cubic-bezier transition timing function: https://codepen.io/jwdunn/pen/VJGzNm
@Jdunn1 confirmed, the UnitBezier kicks some punch and it moves pretty fast as well, thank you.
@jwdunn1 if you're looking for an ES6 version, here's one.
Tip, you should convert them to arrow expressions, and dont minify, this is 2020, we dont need minification :D
ALSO you seemed to forget the "var" part of the EasingFunctions variable.
var EasingFunctions = {
// no easing, no acceleration
linear: t => {
return t;
},
// accelerating from zero velocity
easeInQuad: t => {
return t * t;
},
// decelerating to zero velocity
easeOutQuad: t => {
return t * (2 - t);
},
// acceleration until halfway, then deceleration
easeInOutQuad: t => {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
},
// accelerating from zero velocity
easeInCubic: t => {
return t * t * t;
},
// decelerating to zero velocity
easeOutCubic: t => {
return --t * t * t + 1;
},
// acceleration until halfway, then deceleration
easeInOutCubic: t => {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
},
// accelerating from zero velocity
easeInQuart: t => {
return t * t * t * t;
},
// decelerating to zero velocity
easeOutQuart: t => {
return 1 - --t * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuart: t => {
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
},
// accelerating from zero velocity
easeInQuint: t => {
return t * t * t * t * t;
},
// decelerating to zero velocity
easeOutQuint: t => {
return 1 + --t * t * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuint: t => {
return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
}
};
@skylerspark done! it's a snippet, up to you to var
it or const
it or module.exports=
it or export default
it ;)
@gre Very useful! Thank you very much, Gaëtan!
Very helpful!
really awesome and useful!! Thank you!!
Great gist @gre!
I thought it might be helpful to those who wanted to extend the curves to see how that could be done using higher order functions and increasing the power (Thanks to @lindell for the original snippet! This version however removes the unnecessary Math.abs
, reuses easeIn
for the easeOut
function and changes linear
to reference easeIn
rather than easeInOut
to reduce the complexity for each call (although this definitely isn't an exercise in performance))
const easeIn = p => t => Math.pow(t, p)
const easeOut = p => t => 1 - easeIn(p)(1 - t)
const easeInOut = p => t => t < .5 ? easeIn(p)(t * 2) / 2 : easeOut(p)(t * 2 - 1) / 2 + .5
const Easings = {
linear: easeIn(1),
easeInQuad: easeIn(2),
easeOutQuad: easeOut(2),
easeInOutQuad: easeInOut(2),
easeInCubic: easeIn(3),
easeOutCubic: easeOut(3),
easeInOutCubic: easeInOut(3),
easeInQuart: easeIn(4),
easeOutQuart: easeOut(4),
easeInOutQuart: easeInOut(4),
easeInQuint: easeIn(5),
easeOutQuint: easeOut(5),
easeInOutQuint: easeInOut(5)
}
Knowing this, you can re-write @gre's gist like so:
const Easings = {
linear = t => t,
easeInQuad = t => Math.pow(t, 2),
easeOutQuad = t => 1 - Math.pow(1 - t, 2),
easeInOutQuad = t => t < .5 ? Math.pow(t * 2, 2) / 2 : (1 - Math.pow(1 - (t * 2 - 1), 2)) / 2 + .5,
easeInCubic = t => Math.pow(t, 3),
easeOutCubic = t => 1 - Math.pow(1 - t, 3),
easeInOutCubic = t => t < .5 ? Math.pow(t * 2, 3) / 2 : (1 - Math.pow(1 - (t * 2 - 1), 3)) / 2 + .5,
easeInQuart = t => Math.pow(t, 4),
easeOutQuart = t => 1 - Math.pow(1 - t, 4),
easeInOutQuart = t => t < .5 ? Math.pow(t * 2, 4) / 2 : (1 - Math.pow(1 - (t * 2 - 1), 4)) / 2 + .5,
easeInQuint = t => Math.pow(t, 5),
easeOutQuint = t => 1 - Math.pow(1 - t, 5),
easeInOutQuint = t => t < .5 ? Math.pow(t * 2, 5) / 2 : (1 - Math.pow(1 - (t * 2 - 1), 5)) / 2 + .5
}
Although it is much more verbose, hopefully it's easier to understand for those who don't understand the pre-decrement tricks
Finally you can take the previous example and substitute parts of the equations to reference each other (cleaning it up a little bit)
const linear = t => t
const easeInQuad = t => Math.pow(t, 2)
const easeOutQuad = t => 1 - easeInQuad(1 - t)
const easeInOutQuad = t => t < .5 ? easeInQuad(t * 2) / 2 : easeOutQuad(t * 2 - 1) / 2 + .5
const easeInCubic = t => Math.pow(t, 3)
const easeOutCubic = t => 1 - easeInCubic(1 - t)
const easeInOutCubic = t => t < .5 ? easeInCubic(t * 2) / 2 : easeOutCubic(t * 2 - 1) / 2 + .5
const easeInQuart = t => Math.pow(t, 4)
const easeOutQuart = t => 1 - easeInQuart(1 - t)
const easeInOutQuart = t => t < .5 ? easeInQuart(t * 2) / 2 : easeOutQuart(t * 2 - 1) / 2 + .5
const easeInQuint = t => Math.pow(t, 5)
const easeOutQuint = t => 1 - easeInQuint(1 - t)
const easeInOutQuint = t => t < .5 ? easeInQuint(t * 2) / 2 : easeOutQuint(t * 2 - 1) / 2 + .5
Great thread!
@aarongeorge thanks a lot! this is super useful! specifically, recently I was wondering how to generate it to any pow, I used this to something unrelated to easing but to do "distribution" => https://greweb.me/plots/109 (making my lines reaching more the edges)
An alternative (works somehow)
eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('f d={c:3(n){0 n},g:3(n){0 n*n},a:3(n){0 n*(2-n)},7:3(n){0 n<.5?2*n*n:(4-2*n)*n-1},9:3(n){0 n*n*n},b:3(n){0--n*n*n+1},e:3(n){0 n<.5?4*n*n*n:(n-1)*(2*n-2)*(2*n-2)+1},k:3(n){0 n*n*n*n},h:3(n){0 1- --n*n*n*n},i:3(n){0 n<.5?8*n*n*n*n:1-8*--n*n*n*n},l:3(n){0 n*n*n*n*n},j:3(n){0 1+--n*n*n*n*n},m:3(n){0 n<.5?6*n*n*n*n*n:1+6*--n*n*n*n*n}};',24,24,'return|||function|||16|easeInOutQuad||easeInCubic|easeOutQuad|easeOutCubic|linear|EasingFunctions|easeInOutCubic|var|easeInQuad|easeOutQuart|easeInOutQuart|easeOutQuint|easeInQuart|easeInQuint|easeInOutQuint|'.split('|'),0,{}))
visual cheat sheet https://easings.net/
This is really good but the easing functions are causing me trouble. I would like the first 75% of the count to go very quickly, and then decelerate to a crawl. I what would the proper function be for this? I imagine something like:
const easeOut = t<.75 ? t => t : [SOMETHING] ;
but I don't know what to put there.
this is awesome man, thanks a bunch