/** * 弹窗 center 入场曲线:冲击弹出 → 微回弹 → 落锁 → 等比轻弹收尾(仅 bounce) */ const JELLY_DUR = 0.64; const SEG1 = 0.26; const SEG2 = 0.1; const SEG3 = 0.13; const SEG4 = 0.15; /** @deprecated 由 adaptivePopDuration 替代 */ export const POP_OVERSHOOT_DUR = 0.11; /** @deprecated */ export const POP_SETTLE_BACK_DUR = 0.09; /** @deprecated */ export const POP_JELLY_SETTLE_DUR = 0.22; /** @deprecated */ export const POP_CENTER_POP_IN_DUR = POP_OVERSHOOT_DUR + POP_SETTLE_BACK_DUR; /** @deprecated */ export const POP_JELLY_DURATION = POP_CENTER_POP_IN_DUR + POP_JELLY_SETTLE_DUR; /** @deprecated */ export const POP_OVERSHOOT_SCALE = 1.09; /** @deprecated */ export const POP_JELLY_PANEL_AMP = 0.275; /** 内部子项 reveal 与 center 弹入重叠启动 */ export const POP_REVEAL_START_DELAY = 0.04; /** 阶段1:0 → 峰值,爆发式弹出 */ export function popImpactLaunch(t: number, peak: number): number { return peak * easeOutExpo(clamp01(t)); } /** 阶段1 Y 轴:高处砸落 */ export function popImpactDropY(t: number, lift: number): number { return lift * (1 - easeOutCubic(clamp01(t))); } /** 阶段2:峰值 → 微欠冲(撞击回弹感) */ export function popImpactRecoil(t: number, peak: number, undershoot: number): number { return lerp(peak, undershoot, easeInQuad(clamp01(t))); } /** 阶段3:欠冲 → 1,落锁 */ export function popImpactLock(t: number, undershoot: number): number { return lerp(undershoot, 1, easeOutCubic(clamp01(t))); } /** 阶段4:等比轻弹收尾(衰减正弦,不压扁变形) */ export function popImpactTailBounce(t: number, amp: number): { scale: number; yOff: number } { const phase = clamp01(t); const fade = Math.pow(1 - phase, 0.65); const wave = Math.sin(phase * Math.PI); const bump = wave * fade; return { scale: 1 + amp * 0.06 * bump, yOff: -amp * 4 * bump, }; } /** @deprecated 使用 popImpactTailBounce */ export function popImpactJelly(t: number, amp: number): { sx: number; sy: number } { const { scale } = popImpactTailBounce(t, amp); return { sx: scale, sy: scale }; } /** 弹出透明度闪光 */ export function popImpactOpacity(t: number, start: number): number { const eased = easeOutQuad(clamp01(t)); return lerp(start, 255, eased); } /** @deprecated */ export function popCenterOvershootUniform(t: number, peak = 1.09): number { return popImpactLaunch(t, peak); } /** @deprecated */ export function popCenterSettleUniform(t: number, peak = 1.09): number { return popImpactRecoil(t, peak, 0.98); } /** @deprecated */ export function popJellySettle(t: number, amp = 0.275): { sx: number; sy: number } { return popImpactJelly(t, amp); } /** @deprecated */ export function popCenterPopInYOffset(t: number, lift = 10): number { return popImpactDropY(t, lift); } /** @deprecated */ export function popCenterPopInScale(t: number): number { return popImpactLaunch(t, 1); } /** 子节点错峰:等比缩放 */ export function popItemStaggerScale(t: number, overshoot = 1.9): number { const eased = easeOutBack(clamp01(t), overshoot); return Math.min(eased, 1.08); } export function popItemRevealScale(t: number): number { return popItemStaggerScale(t, 1.9); } export function popItemCelebrateScale(t: number): number { return popItemStaggerScale(t, 2.1); } export function popJellySquash(t: number, amp = 1): { sx: number; sy: number } { const phase = clamp01(t) * JELLY_DUR; let sx: number; let sy: number; if (phase <= SEG1) { const p = quadIn(phase / SEG1); sx = lerp(0.2, 0.75, p); sy = lerp(0.2, 1.4, p); } else if (phase <= SEG1 + SEG2) { const p = quadOut((phase - SEG1) / SEG2); sx = lerp(0.75, 1.6, p); sy = lerp(1.4, 0.4, p); } else if (phase <= SEG1 + SEG2 + SEG3) { const p = easeOutBack((phase - SEG1 - SEG2) / SEG3, 1.7); sx = lerp(1.6, 0.8, p); sy = lerp(0.4, 1.38, p); } else { const p = sineOut((phase - SEG1 - SEG2 - SEG3) / SEG4); sx = lerp(0.8, 1, p); sy = lerp(1.38, 1, p); } if (amp === 1) return { sx, sy }; return { sx: 1 + (sx - 1) * amp, sy: 1 + (sy - 1) * amp }; } export function popJellyLift(t: number, lift = 44): number { const phase = clamp01(t) * JELLY_DUR; if (phase <= SEG1) return lift * (1 - quadIn(phase / SEG1)); return 0; } /** @deprecated */ export function popBounceSquash(t: number, amp = 0.275): { sx: number; sy: number } { return popJellySquash(t, amp); } /** @deprecated */ export function popCenterScale(t: number, overshoot = 1.75): number { const { sx, sy } = popJellySquash(t, 0.35); return (sx + sy) * 0.5; } /** @deprecated */ export function popBounceAngleDeg(_t: number): number { return 0; } export function popEnterYOffset(t: number, lift = 14): number { return popJellyLift(t, lift); } export function popCelebrateYOffset(t: number): number { return popJellyLift(t, 20); } /** @deprecated */ export function popEnterScale(t: number): number { return popCenterScale(t); } /** @deprecated */ export function popCelebrateScale(t: number): number { return popCenterScale(t); } export function popExitScale(t: number): number { return 1 - easeInBack(clamp01(t), 2.0); } export function popExitYOffset(t: number): number { return 8 * easeInBack(clamp01(t), 1.6); } function lerp(a: number, b: number, t: number): number { return a + (b - a) * t; } function clamp01(t: number): number { if (t <= 0) return 0; if (t >= 1) return 1; return t; } function quadIn(t: number): number { return t * t; } function quadOut(t: number): number { return 1 - (1 - t) * (1 - t); } function easeInQuad(t: number): number { return t * t; } function easeOutQuad(t: number): number { return 1 - (1 - t) * (1 - t); } function sineOut(t: number): number { return Math.sin(t * Math.PI / 2); } function easeOutCubic(t: number): number { return 1 - Math.pow(1 - t, 3); } function easeOutExpo(t: number): number { return t >= 1 ? 1 : 1 - Math.pow(2, -10 * t); } function easeOutBack(t: number, overshoot: number): number { const c = overshoot + 1; return 1 + c * Math.pow(t - 1, 3) + overshoot * Math.pow(t - 1, 2); } function easeInBack(t: number, overshoot: number): number { const c = overshoot + 1; return c * t * t * t - overshoot * t * t; }