feat: 动画以曲线参考值为终值

This commit is contained in:
unanmed 2026-03-10 15:17:45 +08:00
parent 555cf96d76
commit f6a065fa3e
3 changed files with 41 additions and 17 deletions

View File

@ -6,7 +6,8 @@ import {
IAnimationPlan,
IExcitableController,
IExcitation,
IAnimatePlanIdentifier
IAnimatePlanIdentifier,
EndRelation
} from './types';
import { linear } from './utils';
@ -27,6 +28,8 @@ interface IAnimaterRawPlan extends IAnimationPlan {
readonly after: Set<IAnimaterTimedInfo>;
/** 兑现函数,当本次动画计划执行完毕后执行 */
readonly resolve: () => void;
/** 终值参考模式 */
readonly end: EndRelation;
}
interface IAnimatingContent extends IAnimaterRawPlan {
@ -161,7 +164,7 @@ export class Animater implements IAnimater {
return this;
}
to(value: number, time: number): this {
to(value: number, time: number, end: EndRelation = EndRelation.Self): this {
if (!this.animatableStatus || !this.currentAnimatable) return this;
this.planning = true;
// 定义动画计划
@ -171,6 +174,7 @@ export class Animater implements IAnimater {
identifier: { content: this.currentAnimatable, index },
curve: this.curveStatus,
targetValue: value,
end,
time,
promise,
resolve,
@ -365,16 +369,20 @@ export class Animater implements IAnimater {
const content = anim.identifier.content;
if (progress >= 1) {
// 动画结束
if (anim.end === EndRelation.Self) {
content.value = anim.targetValue;
} else {
content.value = anim.startValue + anim.curve(1) * anim.diff;
}
anim.resolve();
endedAnimate.add(anim);
// 检查 after
anim.after.forEach(v => afters.add(v));
} else {
const completion = anim.curve(progress);
content.value = completion * anim.diff + anim.startValue;
}
}
// 检查 after
afters.forEach(v => {
this.startAfter({ ...v, arousedTime: payload });
});

View File

@ -153,6 +153,13 @@ export interface IAnimationPlan {
readonly promise: Promise<void>;
}
export const enum EndRelation {
/** 以动画目标值为动画终值 */
Self,
/** 以曲线 `curve(1)` 乘以传入的终值与初值的差作为动画终值 */
Curve
}
export interface IAnimater extends IExcitable<number> {
/**
*
@ -183,7 +190,7 @@ export interface IAnimater extends IExcitable<number> {
* @param value
* @param time
*/
to(value: number, time: number): this;
to(value: number, time: number, end?: EndRelation): this;
/**
*

View File

@ -4,6 +4,7 @@ import {
ExcitationCurve2D,
ExcitationCurve3D,
GeneralExcitationCurve,
IAnimatable,
IExcitable
} from './types';
@ -25,12 +26,20 @@ export function excited<T>(
}
}
/**
*
* @param value
*/
export function excitable(value: number): IAnimatable {
return { value };
}
//#endregion
//#region 曲线计算
/**
* 线 `a(p) + b(p)`
* 线 `h(x) = f(x) + g(x)`
* @param curve1
* @param curve2
*/
@ -42,7 +51,7 @@ export function addCurve(
}
/**
* 线 `a(p) - b(p)`
* 线 `h(x) = f(x) - g(x)`
* @param curve1
* @param curve2
*/
@ -54,7 +63,7 @@ export function subCurve(
}
/**
* 线 `a(p) * b(p)`
* 线 `h(x) = f(x) * g(x)`
* @param curve1
* @param curve2
*/
@ -66,7 +75,7 @@ export function mulCurve(
}
/**
* 线 `a(p) / b(p)`
* 线 `h(x) = f(x) / g(x)`
* @param curve1
* @param curve2
*/
@ -78,7 +87,7 @@ export function divCurve(
}
/**
* 线 `a(p) ** b(p)`
* 线 `h(x) = f(x) ** g(x)`
* @param curve1
* @param curve2
*/
@ -90,7 +99,7 @@ export function powCurve(
}
/**
* 线`a(b(p))`
* 线`h(x) = f(g(x))`
* @param curve1 线
* @param curve2 线
*/
@ -102,7 +111,7 @@ export function compositeCurve(
}
/**
* 线`a(p) + b`
* 线`g(x) = a(x) + b`
* @param curve 线
* @param move
*/
@ -114,7 +123,7 @@ export function moveCurve(
}
/**
* 线`b - a(p)`
* 线`g(x) = b - f(x)`
* @param curve 线
* @param move
*/
@ -126,7 +135,7 @@ export function oppsiteCurve(
}
/**
* 线`a(p) * b`
* 线`g(x) = f(x) * b`
* @param curve 线
* @param scale
*/
@ -322,7 +331,7 @@ export function applyCurveMode(func: ExcitationCurve, mode: CurveMode) {
}
}
/** f(x) = 1 - cos(x * pi/2), x∈[0,1], f(x)∈[0,1] */
/** `f(x) = 1 - cos(x * pi/2), x∈[0,1], f(x)∈[0,1]` */
const sinfunc: ExcitationCurve = p => 1 - Math.cos((p * Math.PI) / 2);
/**
@ -333,7 +342,7 @@ export function sin(mode: CurveMode = CurveMode.EaseIn): ExcitationCurve {
return applyCurveMode(sinfunc, mode);
}
/** f(x) = tan(x * pi/4), x∈[0,1], f(x)∈[0,1] */
/** `f(x) = tan(x * pi/4), x∈[0,1], f(x)∈[0,1]` */
const tanfunc: ExcitationCurve = p => Math.tan((p * Math.PI) / 4);
/**
@ -344,7 +353,7 @@ export function tan(mode: CurveMode = CurveMode.EaseIn): ExcitationCurve {
return applyCurveMode(tanfunc, mode);
}
/** f(x) = sec(x * pi/3) - 1, x∈[0,1], f(x)∈[0,1] */
/** `f(x) = sec(x * pi/3) - 1, x∈[0,1], f(x)∈[0,1]` */
const secfunc: ExcitationCurve = p => 1 / Math.cos((p * Math.PI) / 3) - 1;
/**