import { applyEasing } from '../utils/easing'; import { presetToVector, isRotationPreset } from '../utils/motionPresets'; import type {DistortionArea, Point} from "../types"; /** * 애니메이션 루프 관리 클래스 */ export class AnimationLoop { /** * 영역들의 드래그 벡터를 현재 진행도에 따라 업데이트 * @param areas 왜곡 영역 배열 * @returns 업데이트된 영역 배열 */ public static updateAreaDragVectors( areas: DistortionArea[] ): DistortionArea[] { return areas.map((area) => { const { progress, movement } = area; // duration이 0이거나 프리셋이 'none'이면 애니메이션 없음 if (movement.duration <= 0 || movement.preset === 'none') { return { ...area, dragVector: { x: 0, y: 0 }, }; } // 프리셋이 설정되어 있으면 프리셋 기반 벡터 사용 let baseVector: Point; if (movement.preset) { const strength = movement.strength ?? 0.1; baseVector = presetToVector(movement.preset, strength); } else { // 프리셋 없으면 기존 vectorA 사용 (하위 호환성) baseVector = movement.vectorA; } // 이징 적용 const easedProgress = applyEasing(progress, movement.easing); // 벡터 계산 let dragVector: Point; // 스텝 양자화 (영역 속성에서 가져옴) const snapSteps = area.snapSteps ?? 0; // 회전 프리셋인 경우 원운동 if (movement.preset && isRotationPreset(movement.preset)) { const radius = Math.sqrt(baseVector.x * baseVector.x + baseVector.y * baseVector.y); const direction = movement.preset === 'rotate-cw' ? 1 : -1; if (snapSteps > 0) { // 스텝 양자화: 각도를 이산적 단계로 양자화 const totalAngleSteps = snapSteps * 4; const rawAngle = progress * Math.PI * 2; const quantizedAngle = Math.round(rawAngle / (Math.PI * 2) * totalAngleSteps) / totalAngleSteps * Math.PI * 2; dragVector = { x: Math.cos(quantizedAngle * direction) * radius, y: Math.sin(quantizedAngle * direction) * radius, }; } else { const angle = easedProgress * Math.PI * 2; dragVector = { x: Math.cos(angle * direction) * radius, y: Math.sin(angle * direction) * radius, }; } } else { // 일반 왕복 모션 (sin 기반으로 진짜 좌↔우/상↔하 왕복) // sin(0)=0 → sin(π/2)=1 → sin(π)=0 → sin(3π/2)=-1 → sin(2π)=0 if (snapSteps > 0) { // 스텝 양자화: oscillation 출력을 양자화 // Math.round(sin * N) / N → 한 방향 N단계, 전체 2N+1개 위치 const oscillation = Math.sin(progress * Math.PI * 2); const quantized = Math.round(oscillation * snapSteps) / snapSteps; dragVector = { x: baseVector.x * quantized, y: baseVector.y * quantized, }; } else { const oscillation = Math.sin(easedProgress * Math.PI * 2); dragVector = { x: baseVector.x * oscillation, y: baseVector.y * oscillation, }; } } return { ...area, dragVector, }; }); } /** * 모든 영역의 진행도를 델타 타임만큼 업데이트 * @param areas 왜곡 영역 배열 * @param deltaTime 델타 타임 (초) * @returns 업데이트된 영역 배열 */ public static updateProgress( areas: DistortionArea[], deltaTime: number ): DistortionArea[] { return areas.map((area) => { // duration이 0이면 progress 업데이트 안 함 if (area.movement.duration <= 0) { return area; } let newProgress = area.progress + deltaTime / area.movement.duration; newProgress %= 1.0; // 루프 return { ...area, progress: newProgress, }; }); } }