Coding soon

Coding soon

Easing

easing html
-
<div class="easing"></div>
easing css
-
:root {
	--col: #2563eb;
	--bg: #f8fafc;
	--card: #fff;
	--back: #e2e8f0;
	--text: #000;
}

@media (prefers-color-scheme: dark) {
	:root {
		--col: #60a5fa;
		--bg: #1a1a1a;
		--card: #282828;
		--back: #334155;
		--text: #bababa;
	}
}

.easing {
	font-family: "Courier New", Courier, monospace;
	background: var(--bg-color);
	color: var(--text);
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
	gap: .6rem;
	box-sizing: border-box;
}

.ease {
	position: relative;
	box-sizing: border-box;
	background: var(--card);
	border-radius: 4px;
	display: flex;
	flex-direction: column;
	padding: 0.3rem;
	opacity: 0.4;
	cursor: pointer;
}

.ease:hover {
	opacity: 1;
}

.ease h3 {
	margin: 0;
	font-size: 0.9rem;
	font-weight: normal;
}

.ease svg {
	width: 100%;
	height: 100%;
}

.ease output {
	position: absolute;
	right: .5rem;
	bottom: .5rem;
	opacity: .5;
	font-size: 0.7rem;
	user-select: none;
}

.ease div:last-child {
	height: 4px;
	background: var(--back);
	position: relative;
}

.ease div:last-child > div {
	height: 100%;
	background: var(--col);
}
easing js
+
const PI = Math.PI,
	c1 = 1.70158,
	c2 = c1 * 1.525,
	c3 = c1 + 1,
	c4 = (2 * PI) / 3,
	c5 = (2 * PI) / 4.5;

const create = (tag, ns = "") =>
	ns ? document.createElementNS(ns,
		tag) : document.createElement(tag);
const attr = (el, attrs) =>
	Object.entries(attrs)
	.forEach(([k, v]) => 
		el.setAttribute(k,
			v));
const svg = tag => 
	create(tag,
		"http://www.w3.org/2000/svg");

const bounce = x => {

	const n1 = 7.5625,
		d1 = 2.75;

	if(x < 1 / d1) 
		return n1 * x * x;
	if(x < 2 / d1) 
		return n1 * (x -= 1.5 / d1) * x + 0.75;
	if(x < 2.5 / d1) 
		return n1 * (x -= 2.25 / d1) * x + 0.9375;
	return n1 * (x -= 2.625 / d1) * x + 0.984375;

};

const easings = {
	"linear": x => 
		x,
	"swing": x => 
		0.5 - Math.cos(x * PI) / 2,
	"easeInQuad": x => 
		x * x,
	"easeOutQuad": x => 
		1 - (1 - x) * (1 - x),
	"easeInOutQuad": x =>
		x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2,
			2) / 2,
	"easeInCubic": x => 
		x * x * x,
	"easeOutCubic": x => 
		1 - Math.pow(1 - x,
			3),
	"easeInOutCubic": x =>
		x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2,
			3) / 2,
	"easeInQuart": x => 
		x * x * x * x,
	"easeOutQuart": x => 
		1 - Math.pow(1 - x,
			4),
	"easeInOutQuart": x =>
		x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2,
			4) / 2,
	"easeInQuint": x => 
		x * x * x * x * x,
	"easeOutQuint": x => 
		1 - Math.pow(1 - x,
			5),
	"easeInOutQuint": x =>
		x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.pow(-2 * x + 2,
			5) / 2,
	"easeInSine": x => 
		1 - Math.cos((x * PI) / 2),
	"easeOutSine": x => 
		Math.sin((x * PI) / 2),
	"easeInOutSine": x => 
		-(Math.cos(PI * x) - 1) / 2,
	"easeInExpo": x => 
		(x === 0 ? 0 : Math.pow(2,
			10 * x - 10)),
	"easeOutExpo": x => 
		(x === 1 ? 1 : 1 - Math.pow(2,
			-10 * x)),
	"easeInOutExpo": x =>
		x === 0
			? 0
			: x === 1
				? 1
				: x < 0.5
					? Math.pow(2,
						20 * x - 10) / 2
					: (2 - Math.pow(2,
						-20 * x + 10)) / 2,
	"easeInCirc": x => 
		1 - Math.sqrt(1 - x * x),
	"easeOutCirc": x => 
		Math.sqrt(1 - Math.pow(x - 1,
			2)),
	"easeInOutCirc": x =>
		x < 0.5
			? (1 - Math.sqrt(1 - 4 * x * x)) / 2
			: (Math.sqrt(1 - 4 * (x - 1) * (x - 1)) + 1) / 2,
	"easeInElastic": x =>
		x === 0
			? 0
			: x === 1
				? 1
				: -Math.pow(2,
					10 * x - 10) * Math.sin((x * 10 - 10.75) * c4),
	"easeOutElastic": x =>
		x === 0
			? 0
			: x === 1
				? 1
				: Math.pow(2,
					-10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1,
	"easeInOutElastic": x =>
		x === 0
			? 0
			: x === 1
				? 1
				: x < 0.5
					? -(Math.pow(2,
						20 * x - 10) * Math.sin((20 * x - 11.125) * c5)) / 2
					: (Math.pow(2,
						-20 * x + 10) * Math.sin((20 * x - 11.125) * c5))
					/ 2
			  + 1,
	"easeInBack": x => 
		c3 * x * x * x - c1 * x * x,
	"easeOutBack": x => 
		1 + c3 * Math.pow(x - 1,
			3) + c1 * Math.pow(x - 1,
			2),
	"easeInOutBack": x =>
		x < 0.5
			? (Math.pow(2 * x,
				2) * ((c2 + 1) * 2 * x - c2)) / 2
			: (Math.pow(2 * x - 2,
				2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2,
	"easeInBounce": x => 
		1 - bounce(1 - x),
	"easeOutBounce": bounce,
	"easeInOutBounce": x =>
		x < 0.5 ? (1 - bounce(1 - 2 * x)) / 2 : (1 + bounce(2 * x - 1)) / 2
};

function createEaseViz(name, ease) {

	let minY = Infinity,
		maxY = -Infinity;
	const points = Array.from({ length: 201 },
		(_, i) => {

			const x = i / 200;
			const y = ease(x);

			minY = Math.min(minY,
				y);
			maxY = Math.max(maxY,
				y);
			return [x, y];
	
		});

	const yRange = maxY - minY;
	const normalize = yRange > 1 ? y => 
		(y - minY) / yRange : y => 
		y;

	if(yRange > 1) 
		points.forEach(p => 
			(p[1] = normalize(p[1])));

	const el = create("article");
	el.classList.add("ease");

	attr(el,
		{ role: "button" });

	const svgEl = svg("svg");

	attr(svgEl,
		{ viewBox: "-0.1 -0.1 1.2 1.2" });

	const background = svg("rect");

	attr(background,
		{
			x: "0",
			y: "1",
			width: "1",
			height: "0",
			fill: "var(--back)",
			opacity: "0.4"
		});

	const path = svg("path");

	attr(path,
		{
			"stroke": "var(--col)",
			"fill": "none",
			"stroke-width": "1",
			"vector-effect": "non-scaling-stroke",
			"d": points
			.map((p, i) => 
				`${i ? "L" : "M"} ${p[0]},${1 - p[1]}`)
			.join(" ")
		});

	const dot = svg("circle");

	attr(dot,
		{ r: "0.03", fill: "var(--col)" });

	svgEl.append(background,
		path,
		dot);

	const value = create("output");

	value.textContent = "0.000";

	const line = create("div");
	const lined = create("div");

	line.appendChild(lined);

	let frame;
	const animate = t => {

		const x = Math.min(t / 1500,
			1);
		const y = normalize(ease(x));

		attr(dot,
			{ cx: x, cy: 1 - y });
		attr(background,
			{
				height: y,
				y: 1 - y
			});
		lined.style.width = y * 100 + "%";
		value.textContent = y.toFixed(3)
		.padEnd(5,
			"0");
		if(x < 1) 
			frame = requestAnimationFrame(() => 
				animate(t + 16));
	
	};

	const reset = () => {

		cancelAnimationFrame(frame);
		const y = normalize(ease(0));

		attr(dot,
			{ cx: 0, cy: 1 - y });
		attr(background,
			{
				height: 0,
				y: 1
			});
		lined.style.width = y * 100 + "%";
		value.textContent = y.toFixed(3)
		.padEnd(5,
			"0");
	
	};

	const run = () => {

		reset();
		animate(0);
	
	};

	el.addEventListener("mouseenter",
		run);
	el.addEventListener("click",
		run);
	el.addEventListener("mouseleave",
		reset);

	const title = create("h3");

	title.textContent = name;

	const graph = create("div");

	graph.appendChild(svgEl);

	el.append(title,
		graph,
		value,
		line);
	reset();
	return el;

}

const main = document.querySelector(".easing");

main.append(
	...Object.entries(easings)
	.map(([n, f]) => 
		createEaseViz(n,
			f))
);