import { between } from "@daybrush/utils";
import { EasingFunction } from "./types";
function cubic(y1: number, y2: number, t: number) {
const t2 = 1 - t;
// Bezier Curve Formula
return t * t * t + 3 * t * t * t2 * y2 + 3 * t * t2 * t2 * y1;
}
function solveFromX(x1: number, x2: number, x: number) {
// x 0 ~ 1
// t 0 ~ 1
let t = x;
let solveX = x;
let dx = 1;
while (Math.abs(dx) > 1 / 1000) {
// 예상 t초에 의한 _x값
solveX = cubic(x1, x2, t);
dx = solveX - x;
// 차이가 미세하면 그 값을 t로 지정
if (Math.abs(dx) < 1 / 1000) {
return t;
}
t -= dx / 2;
}
return t;
}
/**
* @namespace easing
*/
/**
* Cubic Bezier curve.
* @memberof easing
* @func bezier
* @param {number} [x1] - point1's x
* @param {number} [y1] - point1's y
* @param {number} [x2] - point2's x
* @param {number} [y2] - point2's y
* @return {function} the curve function
* @example
import {bezier} from "scenejs";
Scene.bezier(0, 0, 1, 1) // LINEAR
Scene.bezier(0.25, 0.1, 0.25, 1) // EASE
*/
export function bezier(x1: number, y1: number, x2: number, y2: number) {
/*
x = f(t)
calculate inverse function by x
t = f-1(x)
*/
const func: EasingFunction = (x: number) => {
const t = solveFromX(x1, x2, between(x, 0, 1));
return cubic(y1, y2, t);
};
func.easingName = `cubic-bezier(${x1},${y1},${x2},${y2})`;
return func;
}
/**
* Specifies a stepping function
* @see {@link https://www.w3schools.com/cssref/css3_pr_animation-timing-function.asp|CSS3 Timing Function}
* @memberof easing
* @func steps
* @param {number} count - point1's x
* @param {"start" | "end"} postion - point1's y
* @return {function} the curve function
* @example
import {steps} from "scenejs";
Scene.steps(1, "start") // Scene.STEP_START
Scene.steps(1, "end") // Scene.STEP_END
*/
export function steps(count: number, position: "start" | "end") {
const func: EasingFunction = (time: number) => {
const level = 1 / count;
if (time >= 1) {
return 1;
}
return (position === "start" ? level : 0) + Math.floor(time / level) * level;
};
func.easingName = `steps(${count}, ${position})`;
return func;
}
/**
* Equivalent to steps(1, start)
* @memberof easing
* @name STEP_START
* @static
* @type {function}
* @example
import {STEP_START} from "scenejs";
Scene.STEP_START // steps(1, start)
*/
export const STEP_START = /*#__PURE__#*/steps(1, "start");
/**
* Equivalent to steps(1, end)
* @memberof easing
* @name STEP_END
* @static
* @type {function}
* @example
import {STEP_END} from "scenejs";
Scene.STEP_END // steps(1, end)
*/
export const STEP_END = /*#__PURE__#*/steps(1, "end");
/**
* Linear Speed (0, 0, 1, 1)
* @memberof easing
* @name LINEAR
* @static
* @type {function}
* @example
import {LINEAR} from "scenejs";
Scene.LINEAR
*/
export const LINEAR = /*#__PURE__#*/bezier(0, 0, 1, 1);
/**
* Ease Speed (0.25, 0.1, 0.25, 1)
* @memberof easing
* @name EASE
* @static
* @type {function}
* @example
import {EASE} from "scenejs";
Scene.EASE
*/
export const EASE = /*#__PURE__#*/bezier(0.25, 0.1, 0.25, 1);
/**
* Ease In Speed (0.42, 0, 1, 1)
* @memberof easing
* @name EASE_IN
* @static
* @type {function}
* @example
import {EASE_IN} from "scenejs";
Scene.EASE_IN
*/
export const EASE_IN = /*#__PURE__#*/bezier(0.42, 0, 1, 1);
/**
* Ease Out Speed (0, 0, 0.58, 1)
* @memberof easing
* @name EASE_OUT
* @static
* @type {function}
* @example
import {EASE_OUT} from "scenejs";
Scene.EASE_OUT
*/
export const EASE_OUT = /*#__PURE__#*/bezier(0, 0, 0.58, 1);
/**
* Ease In Out Speed (0.42, 0, 0.58, 1)
* @memberof easing
* @name EASE_IN_OUT
* @static
* @type {function}
* @example
import {EASE_IN_OUT} from "scenejs";
Scene.EASE_IN_OUT
*/
export const EASE_IN_OUT = /*#__PURE__#*/bezier(0.42, 0, 0.58, 1);