Skip to content

Instantly share code, notes, and snippets.

@hagb4rd
Forked from gre/EasingFunctions.json
Created August 25, 2018 10:09
Show Gist options
  • Save hagb4rd/9f258661bba93267110e905cef25a955 to your computer and use it in GitHub Desktop.
Save hagb4rd/9f258661bba93267110e905cef25a955 to your computer and use it in GitHub Desktop.
DEPRECATED Please use http://github.com/gre/bezier-easing for latest vrrsion.
{
"ease": [0.25, 0.1, 0.25, 1.0],
"linear": [0.00, 0.0, 1.00, 1.0],
"ease-in": [0.42, 0.0, 1.00, 1.0],
"ease-out": [0.00, 0.0, 0.58, 1.0],
"ease-in-out": [0.42, 0.0, 0.58, 1.0]
}
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS and Canvas Bezier timing function editor</title>
<style type="text/css">
* {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
#wrapper {
margin: 0 auto;
width: 550px;
}
#bezierEditor, #clock {
float: left;
}
#clock {
margin-right: 30px;
}
#bezierEditor {
position: relative;
width: 225px;
height: 225px;
}
#yaxis {
position: absolute;
top: 0;
left: 0;
width: 20px;
}
#xaxis {
position: absolute;
bottom: 0;
left: 15px;
height: 20px;
}
#bezier {
position: absolute;
left: 20px;
top: 5px;
}
#info {
margin-top: 10px;
clear: both;
}
p {
padding: 0;
margin: 0;
}
footer {
margin-top: 10px;
padding-top: 10px;
text-align: center;
}
footer a {
color: #09f;
}
dl {
display: block;
margin: 0;
padding: 0;
}
dt {
padding: 0;
margin: 0;
font-weight: bold;
}
dt:after {
content: ': ';
}
</style>
<script type="text/javascript" src="KeySpline.js"></script>
</head>
<body>
<div id="wrapper">
<canvas id="clock" width="200" height="200"></canvas>
<div id="bezierEditor">
<canvas id="bezier" width="200" height="200"></canvas>
<canvas id="yaxis" width="20" height="210"></canvas>
<canvas id="xaxis" width="210" height="20"></canvas>
</div>
<div id="info">
<h2>Usage</h2>
<dl>
<dt>CSS</dt>
<dd><code>transition-timing-function: cubic-bezier(<strong id="transitionTimingFunctionValue"></strong>);</code></dd>
</dl>
<dl>
<dt>JavaScript with KeySpline</dt>
<dd><code>var k = new KeySpline(<strong id="keySplineParams"></strong>); <em style="opacity: 0.5"> ... k.get(time);</em></code></dd>
</dl>
</div>
<footer id="footer">
<a href="http://greweb.me/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/">Read the article</a> -
<a href="https://gist.github.com/1926947">Gist</a>
</footer>
</div>
<script type="text/javascript">(function(){
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
if (window.parent != window) {
var node = document.getElementById("footer");
node.style.display = "none";
}
var cumulativeOffset = function(element) {
var top = 0, left = 0;
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while(element);
return {
top: top,
left: left
};
};
var bezier;
var fun;
var currentStep = [0, 0];
var transitionTimingFunctionValue = document.getElementById("transitionTimingFunctionValue");
var keySplineParams = document.getElementById("keySplineParams");
function setBezier (b) {
bezier = b;
fun = new KeySpline(b[0], b[1], b[2], b[3]).get;
for (var i = 0; i < b.length; ++i)
b[i] = Math.floor(b[i]*100)/100;
var str = b+"";
transitionTimingFunctionValue.innerHTML = str;
keySplineParams.innerHTML = str;
}
function getBezierFunction () {
return fun;
}
(function(){
var canvas = document.getElementById("xaxis");
var ctx = canvas.getContext("2d");
for (var i=0; i<=10; ++i) {
var n = i / 10;
var high = (i % 5 == 0);
var x = 5 + n*(canvas.width-10);
x = Math.floor(x);
ctx.font = "9px monospace";
ctx.textBaseline = "top";
ctx.textAlign = "center";
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(canvas.width, 0);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, 0);
if (high) {
ctx.lineTo(x, 8);
ctx.stroke();
ctx.fillText(""+n, x, 10);
}
else {
ctx.lineTo(x, 4);
ctx.stroke();
}
ctx.textBaseline = "bottom";
ctx.fillText("time", canvas.width-40, 20);
}
}());
(function(){
var canvas = document.getElementById("yaxis");
var ctx = canvas.getContext("2d");
for (var i=0; i<=10; ++i) {
var n = i / 10;
var high = (i % 5 == 0);
var y = 5 + (1-n)*(canvas.height-10);
y = Math.floor(y);
ctx.font = "9px monospace";
ctx.textBaseline = "middle";
ctx.textAlign = "right";
ctx.beginPath();
ctx.moveTo(20, 0);
ctx.lineTo(20, canvas.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(20, y);
ctx.lineTo(16, y);
ctx.stroke();
if (high) {
ctx.fillText(""+Math.floor(n*100), 15, y);
}
}
ctx.textAlign = "left";
ctx.textBaseline = "bottom";
ctx.rotate(Math.PI/2);
ctx.fillText("percentage", 20, 0);
}());
(function(){
var canvas = document.getElementById("clock");
var ctx = canvas.getContext("2d");
var DURATION = 1500;
var LINE_WIDTH = 0.3;
// state variables
var tStart = +new Date();
function setup () {
ctx.scale(canvas.width, canvas.height);
}
function render () {
var bezierf = getBezierFunction();
var now = +new Date();
var t = (now - tStart) % (2*DURATION);
var reverse = t > DURATION;
if (reverse) t -= DURATION;
var x = t / DURATION;
var y = bezierf(x);
currentStep = [ x, y ];
ctx.clearRect(0,0,1,1);
ctx.strokeStyle = 'red';
ctx.lineWidth = LINE_WIDTH;
ctx.beginPath();
ctx.arc(0.5, 0.5, 0.3, 0, y*2*Math.PI, reverse);
ctx.stroke();
}
setup();
(function loop () {
requestAnimFrame(function() {
loop();
render();
}, canvas);
}());
}());
(function(){
var canvas = document.getElementById("bezier");
var ctx = canvas.getContext("2d");
var HANDLE_RADIUS = 0.03;
// state variables
// handles positions
var handle = [ null, [0.25, 0.25], [0.75, 0.75] ];
var draggingHandle = 0;
var hoveringHandle = 0;
var hovering = false;
var stime = +new Date();
var oneHandleClicked = false;
function positionWithE (e) {
var o = cumulativeOffset(canvas);
return { x: relativeX(e.clientX-o.left), y: relativeY(e.clientY-o.top) };
}
function setup() {
canvas.addEventListener("mousedown", function (e) {
var p = positionWithE(e);
var hnum = findHandle(p.x, p.y);
if (hnum) {
draggingHandle = hnum;
oneHandleClicked = true;
}
});
canvas.addEventListener("mouseup", function (e) {
var p = positionWithE(e);
if (draggingHandle) {
setHandle(draggingHandle, p.x, p.y);
draggingHandle = 0;
}
});
canvas.addEventListener("mousemove", function (e) {
var p = positionWithE(e);
if (draggingHandle) {
setHandle(draggingHandle, p.x, p.y);
}
hoveringHandle = draggingHandle || findHandle(p.x, p.y);
});
canvas.addEventListener("mouseover", function (e) {
hovering = true;
hasChanged = true;
});
canvas.addEventListener("mouseout", function (e) {
hovering = false;
hasChanged = true;
draggingHandle = 0;
hoveringHandle = 0;
});
syncBezier();
}
function render () {
var now = +new Date();
ctx.save();
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw grid
ctx.translate(0, canvas.height);
ctx.scale(canvas.width, -canvas.height);
// Draw projections
ctx.lineWidth = 0.01;
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.beginPath();
ctx.moveTo(currentStep[0], 0);
ctx.lineTo(currentStep[0], currentStep[1]);
ctx.lineTo(0, currentStep[1]);
ctx.stroke();
// Draw bezier
ctx.lineWidth = 0.02;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.bezierCurveTo(handle[1][0], handle[1][1], handle[2][0], handle[2][1], 1, 1);
ctx.stroke();
// Draw handle
ctx.strokeStyle = "red";
ctx.fillStyle = "white";
ctx.lineWidth = 0.01;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(handle[1][0], handle[1][1]);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1, 1);
ctx.lineTo(handle[2][0], handle[2][1]);
ctx.stroke();
var r = HANDLE_RADIUS;
if (!oneHandleClicked) {
r += 0.2*HANDLE_RADIUS*Math.cos((stime-now)/150);
}
for (var i=1; i<handle.length; ++i) {
var h = handle[i];
ctx.beginPath();
ctx.arc(h[0], h[1], r, 0, Math.PI*2);
ctx.fillStyle = (hoveringHandle == i) ? "red" : "white";
ctx.fill();
ctx.stroke();
}
ctx.restore();
}
function syncBezier () {
setBezier([ handle[1][0], handle[1][1], handle[2][0], handle[2][1] ]);
}
function findHandle (x, y) {
for (var i=1; i<handle.length; ++i) {
var h = handle[i];
var radius = HANDLE_RADIUS;
var dx = x - h[0];
var dy = y - h[1];
if (dx*dx+dy*dy < HANDLE_RADIUS*HANDLE_RADIUS)
return i;
}
return 0;
}
function setHandle (num, x, y) {
handle [num] = [x, y];
syncBezier();
render();
}
function relativeX (x) {
return x/canvas.width;
}
function relativeY (y) {
return 1-y/canvas.height;
}
setup();
(function loop () {
requestAnimFrame(function() {
loop();
render();
}, canvas);
}());
}());
}());
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-9919624-4']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>
/** MIT License
*
* KeySpline - use bezier curve for transition easing function
* Copyright (c) 2012 Gaetan Renaudeau <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/**
* KeySpline - use bezier curve for transition easing function
* is inspired from Firefox's nsSMILKeySpline.cpp
* Usage:
* var spline = new KeySpline(0.25, 0.1, 0.25, 1.0)
* spline.get(x) => returns the easing value | x must be in [0, 1] range
*/
function KeySpline (mX1, mY1, mX2, mY2) {
this.get = function(aX) {
if (mX1 == mY1 && mX2 == mY2) return aX; // linear
return CalcBezier(GetTForX(aX), mY1, mY2);
}
function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
function C(aA1) { return 3.0 * aA1; }
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
function CalcBezier(aT, aA1, aA2) {
return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
}
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
function GetSlope(aT, aA1, aA2) {
return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
}
function GetTForX(aX) {
// Newton raphson iteration
var aGuessT = aX;
for (var i = 0; i < 4; ++i) {
var currentSlope = GetSlope(aGuessT, mX1, mX2);
if (currentSlope == 0.0) return aGuessT;
var currentX = CalcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment