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

View File

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

View File

@ -4,6 +4,7 @@ import {
ExcitationCurve2D, ExcitationCurve2D,
ExcitationCurve3D, ExcitationCurve3D,
GeneralExcitationCurve, GeneralExcitationCurve,
IAnimatable,
IExcitable IExcitable
} from './types'; } from './types';
@ -25,12 +26,20 @@ export function excited<T>(
} }
} }
/**
*
* @param value
*/
export function excitable(value: number): IAnimatable {
return { value };
}
//#endregion //#endregion
//#region 曲线计算 //#region 曲线计算
/** /**
* 线 `a(p) + b(p)` * 线 `h(x) = f(x) + g(x)`
* @param curve1 * @param curve1
* @param curve2 * @param curve2
*/ */
@ -42,7 +51,7 @@ export function addCurve(
} }
/** /**
* 线 `a(p) - b(p)` * 线 `h(x) = f(x) - g(x)`
* @param curve1 * @param curve1
* @param curve2 * @param curve2
*/ */
@ -54,7 +63,7 @@ export function subCurve(
} }
/** /**
* 线 `a(p) * b(p)` * 线 `h(x) = f(x) * g(x)`
* @param curve1 * @param curve1
* @param curve2 * @param curve2
*/ */
@ -66,7 +75,7 @@ export function mulCurve(
} }
/** /**
* 线 `a(p) / b(p)` * 线 `h(x) = f(x) / g(x)`
* @param curve1 * @param curve1
* @param curve2 * @param curve2
*/ */
@ -78,7 +87,7 @@ export function divCurve(
} }
/** /**
* 线 `a(p) ** b(p)` * 线 `h(x) = f(x) ** g(x)`
* @param curve1 * @param curve1
* @param curve2 * @param curve2
*/ */
@ -90,7 +99,7 @@ export function powCurve(
} }
/** /**
* 线`a(b(p))` * 线`h(x) = f(g(x))`
* @param curve1 线 * @param curve1 线
* @param curve2 线 * @param curve2 线
*/ */
@ -102,7 +111,7 @@ export function compositeCurve(
} }
/** /**
* 线`a(p) + b` * 线`g(x) = a(x) + b`
* @param curve 线 * @param curve 线
* @param move * @param move
*/ */
@ -114,7 +123,7 @@ export function moveCurve(
} }
/** /**
* 线`b - a(p)` * 线`g(x) = b - f(x)`
* @param curve 线 * @param curve 线
* @param move * @param move
*/ */
@ -126,7 +135,7 @@ export function oppsiteCurve(
} }
/** /**
* 线`a(p) * b` * 线`g(x) = f(x) * b`
* @param curve 线 * @param curve 线
* @param scale * @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); 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); 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); 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); 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; const secfunc: ExcitationCurve = p => 1 / Math.cos((p * Math.PI) / 3) - 1;
/** /**