Files
esengine/packages/framework/math/src/Animation/Easing.ts
YHH 155411e743 refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
2025-12-26 14:50:35 +08:00

465 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 缓动函数集合
*
* 提供各种常用的缓动函数,用于创建平滑的动画效果
* 所有函数接受时间参数 t (0-1),返回缓动后的值 (通常0-1)
*/
export class Easing {
// 线性缓动
/**
* 线性缓动(无缓动)
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static linear(t: number): number {
return t;
}
// 二次方缓动 (Quadratic)
/**
* 二次方缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quadIn(t: number): number {
return t * t;
}
/**
* 二次方缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quadOut(t: number): number {
return 1 - (1 - t) * (1 - t);
}
/**
* 二次方缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quadInOut(t: number): number {
return t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);
}
// 三次方缓动 (Cubic)
/**
* 三次方缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static cubicIn(t: number): number {
return t * t * t;
}
/**
* 三次方缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static cubicOut(t: number): number {
return 1 - Math.pow(1 - t, 3);
}
/**
* 三次方缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static cubicInOut(t: number): number {
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
// 四次方缓动 (Quartic)
/**
* 四次方缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quartIn(t: number): number {
return t * t * t * t;
}
/**
* 四次方缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quartOut(t: number): number {
return 1 - Math.pow(1 - t, 4);
}
/**
* 四次方缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quartInOut(t: number): number {
return t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
}
// 五次方缓动 (Quintic)
/**
* 五次方缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quintIn(t: number): number {
return t * t * t * t * t;
}
/**
* 五次方缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quintOut(t: number): number {
return 1 - Math.pow(1 - t, 5);
}
/**
* 五次方缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static quintInOut(t: number): number {
return t < 0.5 ? 16 * t * t * t * t * t : 1 - Math.pow(-2 * t + 2, 5) / 2;
}
// 正弦缓动 (Sine)
/**
* 正弦缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static sineIn(t: number): number {
return 1 - Math.cos((t * Math.PI) / 2);
}
/**
* 正弦缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static sineOut(t: number): number {
return Math.sin((t * Math.PI) / 2);
}
/**
* 正弦缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static sineInOut(t: number): number {
return -(Math.cos(Math.PI * t) - 1) / 2;
}
// 指数缓动 (Exponential)
/**
* 指数缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static expoIn(t: number): number {
return t === 0 ? 0 : Math.pow(2, 10 * (t - 1));
}
/**
* 指数缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static expoOut(t: number): number {
return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
}
/**
* 指数缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static expoInOut(t: number): number {
if (t === 0) return 0;
if (t === 1) return 1;
return t < 0.5
? Math.pow(2, 20 * t - 10) / 2
: (2 - Math.pow(2, -20 * t + 10)) / 2;
}
// 圆形缓动 (Circular)
/**
* 圆形缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static circIn(t: number): number {
return 1 - Math.sqrt(1 - t * t);
}
/**
* 圆形缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static circOut(t: number): number {
return Math.sqrt(1 - (t - 1) * (t - 1));
}
/**
* 圆形缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static circInOut(t: number): number {
return t < 0.5
? (1 - Math.sqrt(1 - 4 * t * t)) / 2
: (Math.sqrt(1 - (-2 * t + 2) * (-2 * t + 2)) + 1) / 2;
}
// 回弹缓动 (Back)
/**
* 回弹缓入
* @param t 时间参数 (0-1)
* @param s 回弹强度默认1.70158
* @returns 缓动值
*/
static backIn(t: number, s: number = 1.70158): number {
const c1 = s;
const c3 = c1 + 1;
return c3 * t * t * t - c1 * t * t;
}
/**
* 回弹缓出
* @param t 时间参数 (0-1)
* @param s 回弹强度默认1.70158
* @returns 缓动值
*/
static backOut(t: number, s: number = 1.70158): number {
const c1 = s;
const c3 = c1 + 1;
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
}
/**
* 回弹缓入缓出
* @param t 时间参数 (0-1)
* @param s 回弹强度默认1.70158
* @returns 缓动值
*/
static backInOut(t: number, s: number = 1.70158): number {
const c1 = s;
const c2 = c1 * 1.525;
return t < 0.5
? (Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
: (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
}
// 弹性缓动 (Elastic)
/**
* 弹性缓入
* @param t 时间参数 (0-1)
* @param amplitude 振幅默认1
* @param period 周期默认0.3
* @returns 缓动值
*/
static elasticIn(t: number, amplitude: number = 1, period: number = 0.3): number {
if (t === 0) return 0;
if (t === 1) return 1;
const s = period / 4;
return -(amplitude * Math.pow(2, 10 * (t - 1)) * Math.sin((t - 1 - s) * (2 * Math.PI) / period));
}
/**
* 弹性缓出
* @param t 时间参数 (0-1)
* @param amplitude 振幅默认1
* @param period 周期默认0.3
* @returns 缓动值
*/
static elasticOut(t: number, amplitude: number = 1, period: number = 0.3): number {
if (t === 0) return 0;
if (t === 1) return 1;
const s = period / 4;
return amplitude * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / period) + 1;
}
/**
* 弹性缓入缓出
* @param t 时间参数 (0-1)
* @param amplitude 振幅默认1
* @param period 周期默认0.45
* @returns 缓动值
*/
static elasticInOut(t: number, amplitude: number = 1, period: number = 0.45): number {
if (t === 0) return 0;
if (t === 1) return 1;
const s = period / 4;
if (t < 0.5) {
return -0.5 * (amplitude * Math.pow(2, 10 * (2 * t - 1)) * Math.sin((2 * t - 1 - s) * (2 * Math.PI) / period));
}
return amplitude * Math.pow(2, -10 * (2 * t - 1)) * Math.sin((2 * t - 1 - s) * (2 * Math.PI) / period) * 0.5 + 1;
}
// 跳跃缓动 (Bounce)
/**
* 跳跃缓入
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static bounceIn(t: number): number {
return 1 - Easing.bounceOut(1 - t);
}
/**
* 跳跃缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static bounceOut(t: number): number {
const n1 = 7.5625;
const d1 = 2.75;
if (t < 1 / d1) {
return n1 * t * t;
} else if (t < 2 / d1) {
return n1 * (t -= 1.5 / d1) * t + 0.75;
} else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else {
return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
}
/**
* 跳跃缓入缓出
* @param t 时间参数 (0-1)
* @returns 缓动值
*/
static bounceInOut(t: number): number {
return t < 0.5
? (1 - Easing.bounceOut(1 - 2 * t)) / 2
: (1 + Easing.bounceOut(2 * t - 1)) / 2;
}
// 组合缓动
/**
* 创建自定义缓动函数(组合多个缓动)
* @param easingFunctions 缓动函数数组
* @param weights 权重数组,默认均等
* @returns 组合后的缓动函数
*/
static combine(
easingFunctions: ((t: number) => number)[],
weights?: number[]
): (t: number) => number {
if (!weights) {
weights = new Array(easingFunctions.length).fill(1 / easingFunctions.length);
}
return (t: number): number => {
let result = 0;
for (let i = 0; i < easingFunctions.length; i++) {
result += easingFunctions[i](t) * (weights![i] || 0);
}
return result;
};
}
/**
* 创建分段缓动函数
* @param segments 分段配置数组,每段包含 {duration, easing}
* @returns 分段缓动函数
*/
static piecewise(segments: Array<{duration: number; easing: (t: number) => number}>): (t: number) => number {
// 计算总持续时间
const totalDuration = segments.reduce((sum, seg) => sum + seg.duration, 0);
// 归一化持续时间
const normalizedSegments = segments.map((seg) => ({
...seg,
duration: seg.duration / totalDuration
}));
return (t: number): number => {
let accumulatedTime = 0;
for (const segment of normalizedSegments) {
if (t <= accumulatedTime + segment.duration) {
const localT = (t - accumulatedTime) / segment.duration;
return segment.easing(Math.max(0, Math.min(1, localT)));
}
accumulatedTime += segment.duration;
}
// 如果超出范围,返回最后一段的结束值
return normalizedSegments[normalizedSegments.length - 1].easing(1);
};
}
/**
* 创建反向缓动函数
* @param easing 原缓动函数
* @returns 反向缓动函数
*/
static reverse(easing: (t: number) => number): (t: number) => number {
return (t: number): number => 1 - easing(1 - t);
}
/**
* 创建镜像缓动函数(先正向再反向)
* @param easing 原缓动函数
* @returns 镜像缓动函数
*/
static mirror(easing: (t: number) => number): (t: number) => number {
return (t: number): number => {
if (t < 0.5) {
return easing(t * 2);
} else {
return easing(2 - t * 2);
}
};
}
// 常用预设
/** 平滑进入常用于UI动画 */
static readonly smoothIn = Easing.quadOut;
/** 平滑退出常用于UI动画 */
static readonly smoothOut = Easing.quadIn;
/** 快速进入(常用于出现动画) */
static readonly quickIn = Easing.cubicOut;
/** 快速退出(常用于消失动画) */
static readonly quickOut = Easing.cubicIn;
/** 自然运动(模拟物理) */
static readonly natural = Easing.quartOut;
/** 强调效果(吸引注意力) */
static readonly emphasize = Easing.backOut;
}