feat: Add motion presets for distortion animations
- 왜곡 애니메이션에 사용할 수 있는 다양한 모션 프리셋(horizontal, vertical, rotate-cw 등)을 추가했습니다. - `DistortionMovement` 인터페이스에 `preset`과 `strength` 옵션을 추가하여 모션 프리셋을 설정할 수 있도록 변경했습니다. - `presetToVector` 함수와 `isRotationPreset` 함수를 추가하여 모션 프리셋 로직을 구현했습니다. - `AnimationLoop` 클래스에서 모션 프리셋을 적용하여 `vectorA`를 계산하도록 수정했습니다.
This commit is contained in:
parent
bbbb49aa1d
commit
f6ad8b11b0
26
dist/index.d.mts
vendored
26
dist/index.d.mts
vendored
@ -12,18 +12,26 @@ interface Point {
|
|||||||
* 애니메이션 이징 함수 타입
|
* 애니메이션 이징 함수 타입
|
||||||
*/
|
*/
|
||||||
type EasingFunction = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'easeInQuad' | 'easeOutQuad';
|
type EasingFunction = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'easeInQuad' | 'easeOutQuad';
|
||||||
|
/**
|
||||||
|
* 모션 프리셋 타입
|
||||||
|
*/
|
||||||
|
type MotionPreset = 'none' | 'horizontal' | 'vertical' | 'rotate-cw' | 'rotate-ccw' | 'pulse' | 'diagonal-1' | 'diagonal-2';
|
||||||
/**
|
/**
|
||||||
* 왜곡 애니메이션 움직임 설정
|
* 왜곡 애니메이션 움직임 설정
|
||||||
*/
|
*/
|
||||||
interface DistortionMovement {
|
interface DistortionMovement {
|
||||||
/** 왜곡 시작 벡터 */
|
/** 모션 프리셋 (vectorA, vectorB 대신 사용) */
|
||||||
|
preset?: MotionPreset;
|
||||||
|
/** 왜곡 시작 벡터 (preset 없을 때 사용) */
|
||||||
vectorA: Point;
|
vectorA: Point;
|
||||||
/** 왜곡 종료 벡터 */
|
/** 왜곡 종료 벡터 (preset 없을 때 사용, 현재는 미사용) */
|
||||||
vectorB: Point;
|
vectorB: Point;
|
||||||
/** 애니메이션 지속 시간 (초) */
|
/** 애니메이션 지속 시간 (초) */
|
||||||
duration: number;
|
duration: number;
|
||||||
/** 적용할 이징 함수 */
|
/** 적용할 이징 함수 */
|
||||||
easing: EasingFunction;
|
easing: EasingFunction;
|
||||||
|
/** 모션 강도 (프리셋 적용 시 벡터 크기 조절용, 기본값: 0.1) */
|
||||||
|
strength?: number;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 사각형 포인트와 애니메이션 설정을 포함하는 왜곡 영역
|
* 사각형 포인트와 애니메이션 설정을 포함하는 왜곡 영역
|
||||||
@ -376,6 +384,18 @@ declare const DEFAULT_AREA: {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모션 프리셋을 벡터로 변환
|
||||||
|
* @param preset 모션 프리셋
|
||||||
|
* @param strength 모션 강도 (기본값: 0.1)
|
||||||
|
* @returns 계산된 벡터 (vectorA)
|
||||||
|
*/
|
||||||
|
declare function presetToVector(preset: MotionPreset, strength?: number): Point;
|
||||||
|
/**
|
||||||
|
* 프리셋이 회전 타입인지 확인
|
||||||
|
*/
|
||||||
|
declare function isRotationPreset(preset?: MotionPreset): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Three.js 씬 관리 클래스
|
* Three.js 씬 관리 클래스
|
||||||
*/
|
*/
|
||||||
@ -539,4 +559,4 @@ declare const useMouseInteraction: (containerRef: React.RefObject<HTMLElement |
|
|||||||
reset: () => void;
|
reset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { ANIMATION_CONFIG, AnimationLoop, type AnimationState, type AnimationTicker, type AreaBounds, DEFAULT_AREA, type DistortionArea, DistortionEditor, type DistortionEditorProps, type DistortionMovement, type EasingFunction, type EditMode, type EditorState, ImageDistortion, type ImageDistortionProps, type MouseInteractionConfig, type MouseState, type Point, SHADER_CONFIG, type ShaderConfig, ShaderManager, type ShaderUniforms, SpringPhysics, type SpringPhysicsConfig, type SpringState, ThreeScene, applyEasing, useAnimationFrame, useDistortionEditor, useMouseInteraction, useMouseVelocity };
|
export { ANIMATION_CONFIG, AnimationLoop, type AnimationState, type AnimationTicker, type AreaBounds, DEFAULT_AREA, type DistortionArea, DistortionEditor, type DistortionEditorProps, type DistortionMovement, type EasingFunction, type EditMode, type EditorState, ImageDistortion, type ImageDistortionProps, type MotionPreset, type MouseInteractionConfig, type MouseState, type Point, SHADER_CONFIG, type ShaderConfig, ShaderManager, type ShaderUniforms, SpringPhysics, type SpringPhysicsConfig, type SpringState, ThreeScene, applyEasing, isRotationPreset, presetToVector, useAnimationFrame, useDistortionEditor, useMouseInteraction, useMouseVelocity };
|
||||||
|
|||||||
26
dist/index.d.ts
vendored
26
dist/index.d.ts
vendored
@ -12,18 +12,26 @@ interface Point {
|
|||||||
* 애니메이션 이징 함수 타입
|
* 애니메이션 이징 함수 타입
|
||||||
*/
|
*/
|
||||||
type EasingFunction = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'easeInQuad' | 'easeOutQuad';
|
type EasingFunction = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'easeInQuad' | 'easeOutQuad';
|
||||||
|
/**
|
||||||
|
* 모션 프리셋 타입
|
||||||
|
*/
|
||||||
|
type MotionPreset = 'none' | 'horizontal' | 'vertical' | 'rotate-cw' | 'rotate-ccw' | 'pulse' | 'diagonal-1' | 'diagonal-2';
|
||||||
/**
|
/**
|
||||||
* 왜곡 애니메이션 움직임 설정
|
* 왜곡 애니메이션 움직임 설정
|
||||||
*/
|
*/
|
||||||
interface DistortionMovement {
|
interface DistortionMovement {
|
||||||
/** 왜곡 시작 벡터 */
|
/** 모션 프리셋 (vectorA, vectorB 대신 사용) */
|
||||||
|
preset?: MotionPreset;
|
||||||
|
/** 왜곡 시작 벡터 (preset 없을 때 사용) */
|
||||||
vectorA: Point;
|
vectorA: Point;
|
||||||
/** 왜곡 종료 벡터 */
|
/** 왜곡 종료 벡터 (preset 없을 때 사용, 현재는 미사용) */
|
||||||
vectorB: Point;
|
vectorB: Point;
|
||||||
/** 애니메이션 지속 시간 (초) */
|
/** 애니메이션 지속 시간 (초) */
|
||||||
duration: number;
|
duration: number;
|
||||||
/** 적용할 이징 함수 */
|
/** 적용할 이징 함수 */
|
||||||
easing: EasingFunction;
|
easing: EasingFunction;
|
||||||
|
/** 모션 강도 (프리셋 적용 시 벡터 크기 조절용, 기본값: 0.1) */
|
||||||
|
strength?: number;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 사각형 포인트와 애니메이션 설정을 포함하는 왜곡 영역
|
* 사각형 포인트와 애니메이션 설정을 포함하는 왜곡 영역
|
||||||
@ -376,6 +384,18 @@ declare const DEFAULT_AREA: {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모션 프리셋을 벡터로 변환
|
||||||
|
* @param preset 모션 프리셋
|
||||||
|
* @param strength 모션 강도 (기본값: 0.1)
|
||||||
|
* @returns 계산된 벡터 (vectorA)
|
||||||
|
*/
|
||||||
|
declare function presetToVector(preset: MotionPreset, strength?: number): Point;
|
||||||
|
/**
|
||||||
|
* 프리셋이 회전 타입인지 확인
|
||||||
|
*/
|
||||||
|
declare function isRotationPreset(preset?: MotionPreset): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Three.js 씬 관리 클래스
|
* Three.js 씬 관리 클래스
|
||||||
*/
|
*/
|
||||||
@ -539,4 +559,4 @@ declare const useMouseInteraction: (containerRef: React.RefObject<HTMLElement |
|
|||||||
reset: () => void;
|
reset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { ANIMATION_CONFIG, AnimationLoop, type AnimationState, type AnimationTicker, type AreaBounds, DEFAULT_AREA, type DistortionArea, DistortionEditor, type DistortionEditorProps, type DistortionMovement, type EasingFunction, type EditMode, type EditorState, ImageDistortion, type ImageDistortionProps, type MouseInteractionConfig, type MouseState, type Point, SHADER_CONFIG, type ShaderConfig, ShaderManager, type ShaderUniforms, SpringPhysics, type SpringPhysicsConfig, type SpringState, ThreeScene, applyEasing, useAnimationFrame, useDistortionEditor, useMouseInteraction, useMouseVelocity };
|
export { ANIMATION_CONFIG, AnimationLoop, type AnimationState, type AnimationTicker, type AreaBounds, DEFAULT_AREA, type DistortionArea, DistortionEditor, type DistortionEditorProps, type DistortionMovement, type EasingFunction, type EditMode, type EditorState, ImageDistortion, type ImageDistortionProps, type MotionPreset, type MouseInteractionConfig, type MouseState, type Point, SHADER_CONFIG, type ShaderConfig, ShaderManager, type ShaderUniforms, SpringPhysics, type SpringPhysicsConfig, type SpringState, ThreeScene, applyEasing, isRotationPreset, presetToVector, useAnimationFrame, useDistortionEditor, useMouseInteraction, useMouseVelocity };
|
||||||
|
|||||||
59
dist/index.js
vendored
59
dist/index.js
vendored
@ -40,6 +40,8 @@ __export(index_exports, {
|
|||||||
SpringPhysics: () => SpringPhysics,
|
SpringPhysics: () => SpringPhysics,
|
||||||
ThreeScene: () => ThreeScene,
|
ThreeScene: () => ThreeScene,
|
||||||
applyEasing: () => applyEasing,
|
applyEasing: () => applyEasing,
|
||||||
|
isRotationPreset: () => isRotationPreset,
|
||||||
|
presetToVector: () => presetToVector,
|
||||||
useAnimationFrame: () => useAnimationFrame,
|
useAnimationFrame: () => useAnimationFrame,
|
||||||
useDistortionEditor: () => useDistortionEditor,
|
useDistortionEditor: () => useDistortionEditor,
|
||||||
useMouseInteraction: () => useMouseInteraction,
|
useMouseInteraction: () => useMouseInteraction,
|
||||||
@ -244,6 +246,34 @@ var applyEasing = (progress, easingType) => {
|
|||||||
return easingFunctions[easingType](clampedProgress);
|
return easingFunctions[easingType](clampedProgress);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// src/utils/motionPresets.ts
|
||||||
|
function presetToVector(preset, strength = 0.1) {
|
||||||
|
switch (preset) {
|
||||||
|
case "none":
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
case "horizontal":
|
||||||
|
return { x: strength, y: 0 };
|
||||||
|
case "vertical":
|
||||||
|
return { x: 0, y: strength };
|
||||||
|
case "rotate-cw":
|
||||||
|
return { x: strength, y: 0 };
|
||||||
|
case "rotate-ccw":
|
||||||
|
return { x: -strength, y: 0 };
|
||||||
|
case "pulse":
|
||||||
|
return { x: strength, y: strength };
|
||||||
|
case "diagonal-1":
|
||||||
|
return { x: strength * 0.707, y: strength * 0.707 };
|
||||||
|
// √2/2 ≈ 0.707
|
||||||
|
case "diagonal-2":
|
||||||
|
return { x: strength * 0.707, y: -strength * 0.707 };
|
||||||
|
default:
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isRotationPreset(preset) {
|
||||||
|
return preset === "rotate-cw" || preset === "rotate-ccw";
|
||||||
|
}
|
||||||
|
|
||||||
// src/engine/AnimationLoop.ts
|
// src/engine/AnimationLoop.ts
|
||||||
var AnimationLoop = class {
|
var AnimationLoop = class {
|
||||||
/**
|
/**
|
||||||
@ -254,27 +284,44 @@ var AnimationLoop = class {
|
|||||||
static updateAreaDragVectors(areas) {
|
static updateAreaDragVectors(areas) {
|
||||||
return areas.map((area) => {
|
return areas.map((area) => {
|
||||||
const { progress, movement } = area;
|
const { progress, movement } = area;
|
||||||
if (movement.duration <= 0) {
|
if (movement.duration <= 0 || movement.preset === "none") {
|
||||||
return {
|
return {
|
||||||
...area,
|
...area,
|
||||||
dragVector: { x: 0, y: 0 }
|
dragVector: { x: 0, y: 0 }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let baseVector;
|
||||||
|
if (movement.preset) {
|
||||||
|
const strength = movement.strength ?? 0.1;
|
||||||
|
baseVector = presetToVector(movement.preset, strength);
|
||||||
|
} else {
|
||||||
|
baseVector = movement.vectorA;
|
||||||
|
}
|
||||||
const easedProgress = applyEasing(progress, movement.easing);
|
const easedProgress = applyEasing(progress, movement.easing);
|
||||||
let dragVector;
|
let dragVector;
|
||||||
|
if (movement.preset && isRotationPreset(movement.preset)) {
|
||||||
|
const angle = easedProgress * Math.PI * 2;
|
||||||
|
const radius = Math.sqrt(baseVector.x * baseVector.x + baseVector.y * baseVector.y);
|
||||||
|
const direction = movement.preset === "rotate-cw" ? 1 : -1;
|
||||||
|
dragVector = {
|
||||||
|
x: Math.cos(angle * direction) * radius,
|
||||||
|
y: Math.sin(angle * direction) * radius
|
||||||
|
};
|
||||||
|
} else {
|
||||||
if (easedProgress < 0.5) {
|
if (easedProgress < 0.5) {
|
||||||
const t = easedProgress * 2;
|
const t = easedProgress * 2;
|
||||||
dragVector = {
|
dragVector = {
|
||||||
x: movement.vectorA.x * t,
|
x: baseVector.x * t,
|
||||||
y: movement.vectorA.y * t
|
y: baseVector.y * t
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const t = (easedProgress - 0.5) * 2;
|
const t = (easedProgress - 0.5) * 2;
|
||||||
dragVector = {
|
dragVector = {
|
||||||
x: movement.vectorA.x * (1 - t),
|
x: baseVector.x * (1 - t),
|
||||||
y: movement.vectorA.y * (1 - t)
|
y: baseVector.y * (1 - t)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...area,
|
...area,
|
||||||
dragVector
|
dragVector
|
||||||
@ -1683,6 +1730,8 @@ var DEFAULT_EDITOR_CANVAS_STYLE = {
|
|||||||
SpringPhysics,
|
SpringPhysics,
|
||||||
ThreeScene,
|
ThreeScene,
|
||||||
applyEasing,
|
applyEasing,
|
||||||
|
isRotationPreset,
|
||||||
|
presetToVector,
|
||||||
useAnimationFrame,
|
useAnimationFrame,
|
||||||
useDistortionEditor,
|
useDistortionEditor,
|
||||||
useMouseInteraction,
|
useMouseInteraction,
|
||||||
|
|||||||
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
57
dist/index.mjs
vendored
57
dist/index.mjs
vendored
@ -195,6 +195,34 @@ var applyEasing = (progress, easingType) => {
|
|||||||
return easingFunctions[easingType](clampedProgress);
|
return easingFunctions[easingType](clampedProgress);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// src/utils/motionPresets.ts
|
||||||
|
function presetToVector(preset, strength = 0.1) {
|
||||||
|
switch (preset) {
|
||||||
|
case "none":
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
case "horizontal":
|
||||||
|
return { x: strength, y: 0 };
|
||||||
|
case "vertical":
|
||||||
|
return { x: 0, y: strength };
|
||||||
|
case "rotate-cw":
|
||||||
|
return { x: strength, y: 0 };
|
||||||
|
case "rotate-ccw":
|
||||||
|
return { x: -strength, y: 0 };
|
||||||
|
case "pulse":
|
||||||
|
return { x: strength, y: strength };
|
||||||
|
case "diagonal-1":
|
||||||
|
return { x: strength * 0.707, y: strength * 0.707 };
|
||||||
|
// √2/2 ≈ 0.707
|
||||||
|
case "diagonal-2":
|
||||||
|
return { x: strength * 0.707, y: -strength * 0.707 };
|
||||||
|
default:
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isRotationPreset(preset) {
|
||||||
|
return preset === "rotate-cw" || preset === "rotate-ccw";
|
||||||
|
}
|
||||||
|
|
||||||
// src/engine/AnimationLoop.ts
|
// src/engine/AnimationLoop.ts
|
||||||
var AnimationLoop = class {
|
var AnimationLoop = class {
|
||||||
/**
|
/**
|
||||||
@ -205,27 +233,44 @@ var AnimationLoop = class {
|
|||||||
static updateAreaDragVectors(areas) {
|
static updateAreaDragVectors(areas) {
|
||||||
return areas.map((area) => {
|
return areas.map((area) => {
|
||||||
const { progress, movement } = area;
|
const { progress, movement } = area;
|
||||||
if (movement.duration <= 0) {
|
if (movement.duration <= 0 || movement.preset === "none") {
|
||||||
return {
|
return {
|
||||||
...area,
|
...area,
|
||||||
dragVector: { x: 0, y: 0 }
|
dragVector: { x: 0, y: 0 }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let baseVector;
|
||||||
|
if (movement.preset) {
|
||||||
|
const strength = movement.strength ?? 0.1;
|
||||||
|
baseVector = presetToVector(movement.preset, strength);
|
||||||
|
} else {
|
||||||
|
baseVector = movement.vectorA;
|
||||||
|
}
|
||||||
const easedProgress = applyEasing(progress, movement.easing);
|
const easedProgress = applyEasing(progress, movement.easing);
|
||||||
let dragVector;
|
let dragVector;
|
||||||
|
if (movement.preset && isRotationPreset(movement.preset)) {
|
||||||
|
const angle = easedProgress * Math.PI * 2;
|
||||||
|
const radius = Math.sqrt(baseVector.x * baseVector.x + baseVector.y * baseVector.y);
|
||||||
|
const direction = movement.preset === "rotate-cw" ? 1 : -1;
|
||||||
|
dragVector = {
|
||||||
|
x: Math.cos(angle * direction) * radius,
|
||||||
|
y: Math.sin(angle * direction) * radius
|
||||||
|
};
|
||||||
|
} else {
|
||||||
if (easedProgress < 0.5) {
|
if (easedProgress < 0.5) {
|
||||||
const t = easedProgress * 2;
|
const t = easedProgress * 2;
|
||||||
dragVector = {
|
dragVector = {
|
||||||
x: movement.vectorA.x * t,
|
x: baseVector.x * t,
|
||||||
y: movement.vectorA.y * t
|
y: baseVector.y * t
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const t = (easedProgress - 0.5) * 2;
|
const t = (easedProgress - 0.5) * 2;
|
||||||
dragVector = {
|
dragVector = {
|
||||||
x: movement.vectorA.x * (1 - t),
|
x: baseVector.x * (1 - t),
|
||||||
y: movement.vectorA.y * (1 - t)
|
y: baseVector.y * (1 - t)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...area,
|
...area,
|
||||||
dragVector
|
dragVector
|
||||||
@ -1633,6 +1678,8 @@ export {
|
|||||||
SpringPhysics,
|
SpringPhysics,
|
||||||
ThreeScene,
|
ThreeScene,
|
||||||
applyEasing,
|
applyEasing,
|
||||||
|
isRotationPreset,
|
||||||
|
presetToVector,
|
||||||
useAnimationFrame,
|
useAnimationFrame,
|
||||||
useDistortionEditor,
|
useDistortionEditor,
|
||||||
useMouseInteraction,
|
useMouseInteraction,
|
||||||
|
|||||||
2
dist/index.mjs.map
vendored
2
dist/index.mjs.map
vendored
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
|||||||
import { applyEasing } from '../utils/easing';
|
import { applyEasing } from '../utils/easing';
|
||||||
|
import { presetToVector, isRotationPreset } from '../utils/motionPresets';
|
||||||
import type {DistortionArea, Point} from "../types";
|
import type {DistortionArea, Point} from "../types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,35 +17,57 @@ export class AnimationLoop {
|
|||||||
return areas.map((area) => {
|
return areas.map((area) => {
|
||||||
const { progress, movement } = area;
|
const { progress, movement } = area;
|
||||||
|
|
||||||
// duration이 0이면 애니메이션 없음 (dragVector를 0으로 유지)
|
// duration이 0이거나 프리셋이 'none'이면 애니메이션 없음
|
||||||
if (movement.duration <= 0) {
|
if (movement.duration <= 0 || movement.preset === 'none') {
|
||||||
return {
|
return {
|
||||||
...area,
|
...area,
|
||||||
dragVector: { x: 0, y: 0 },
|
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);
|
const easedProgress = applyEasing(progress, movement.easing);
|
||||||
|
|
||||||
// 벡터 간 보간
|
// 벡터 계산
|
||||||
let dragVector: Point;
|
let dragVector: Point;
|
||||||
|
|
||||||
if (easedProgress < 0.5) {
|
// 회전 프리셋인 경우 원운동
|
||||||
// 0.0 -> 0.5: 0에서 vectorA로 보간
|
if (movement.preset && isRotationPreset(movement.preset)) {
|
||||||
const t = easedProgress * 2;
|
const angle = easedProgress * Math.PI * 2;
|
||||||
|
const radius = Math.sqrt(baseVector.x * baseVector.x + baseVector.y * baseVector.y);
|
||||||
|
const direction = movement.preset === 'rotate-cw' ? 1 : -1;
|
||||||
dragVector = {
|
dragVector = {
|
||||||
x: movement.vectorA.x * t,
|
x: Math.cos(angle * direction) * radius,
|
||||||
y: movement.vectorA.y * t,
|
y: Math.sin(angle * direction) * radius,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// 0.5 -> 1.0: vectorA에서 0으로 보간
|
// 일반 왕복 모션
|
||||||
|
if (easedProgress < 0.5) {
|
||||||
|
// 0.0 -> 0.5: 0에서 baseVector로 보간
|
||||||
|
const t = easedProgress * 2;
|
||||||
|
dragVector = {
|
||||||
|
x: baseVector.x * t,
|
||||||
|
y: baseVector.y * t,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 0.5 -> 1.0: baseVector에서 0으로 보간
|
||||||
const t = (easedProgress - 0.5) * 2;
|
const t = (easedProgress - 0.5) * 2;
|
||||||
dragVector = {
|
dragVector = {
|
||||||
x: movement.vectorA.x * (1 - t),
|
x: baseVector.x * (1 - t),
|
||||||
y: movement.vectorA.y * (1 - t),
|
y: baseVector.y * (1 - t),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...area,
|
...area,
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export { useDistortionEditor } from './editor';
|
|||||||
export type {
|
export type {
|
||||||
Point,
|
Point,
|
||||||
EasingFunction,
|
EasingFunction,
|
||||||
|
MotionPreset,
|
||||||
DistortionMovement,
|
DistortionMovement,
|
||||||
DistortionArea,
|
DistortionArea,
|
||||||
AreaBounds,
|
AreaBounds,
|
||||||
@ -31,6 +32,7 @@ export type {
|
|||||||
// 유틸리티 함수
|
// 유틸리티 함수
|
||||||
export { applyEasing } from './utils/easing';
|
export { applyEasing } from './utils/easing';
|
||||||
export { SHADER_CONFIG, ANIMATION_CONFIG, DEFAULT_AREA } from './utils/constants';
|
export { SHADER_CONFIG, ANIMATION_CONFIG, DEFAULT_AREA } from './utils/constants';
|
||||||
|
export { presetToVector, isRotationPreset } from './utils/motionPresets';
|
||||||
|
|
||||||
// 엔진 클래스 (고급 사용자용)
|
// 엔진 클래스 (고급 사용자용)
|
||||||
export { ThreeScene } from './engine/ThreeScene';
|
export { ThreeScene } from './engine/ThreeScene';
|
||||||
|
|||||||
@ -17,18 +17,35 @@ export type EasingFunction =
|
|||||||
| 'easeInQuad'
|
| 'easeInQuad'
|
||||||
| 'easeOutQuad';
|
| 'easeOutQuad';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모션 프리셋 타입
|
||||||
|
*/
|
||||||
|
export type MotionPreset =
|
||||||
|
| 'none' // 없음 (애니메이션 없음)
|
||||||
|
| 'horizontal' // 좌우 왕복
|
||||||
|
| 'vertical' // 상하 왕복
|
||||||
|
| 'rotate-cw' // 시계방향 회전
|
||||||
|
| 'rotate-ccw' // 반시계방향 회전
|
||||||
|
| 'pulse' // 펄스 (확대/축소)
|
||||||
|
| 'diagonal-1' // 대각선 (좌상→우하)
|
||||||
|
| 'diagonal-2'; // 대각선 (우상→좌하)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 왜곡 애니메이션 움직임 설정
|
* 왜곡 애니메이션 움직임 설정
|
||||||
*/
|
*/
|
||||||
export interface DistortionMovement {
|
export interface DistortionMovement {
|
||||||
/** 왜곡 시작 벡터 */
|
/** 모션 프리셋 (vectorA, vectorB 대신 사용) */
|
||||||
|
preset?: MotionPreset;
|
||||||
|
/** 왜곡 시작 벡터 (preset 없을 때 사용) */
|
||||||
vectorA: Point;
|
vectorA: Point;
|
||||||
/** 왜곡 종료 벡터 */
|
/** 왜곡 종료 벡터 (preset 없을 때 사용, 현재는 미사용) */
|
||||||
vectorB: Point;
|
vectorB: Point;
|
||||||
/** 애니메이션 지속 시간 (초) */
|
/** 애니메이션 지속 시간 (초) */
|
||||||
duration: number;
|
duration: number;
|
||||||
/** 적용할 이징 함수 */
|
/** 적용할 이징 함수 */
|
||||||
easing: EasingFunction;
|
easing: EasingFunction;
|
||||||
|
/** 모션 강도 (프리셋 적용 시 벡터 크기 조절용, 기본값: 0.1) */
|
||||||
|
strength?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
53
src/utils/motionPresets.ts
Normal file
53
src/utils/motionPresets.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import type {MotionPreset, Point} from '../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 모션 프리셋을 벡터로 변환
|
||||||
|
* @param preset 모션 프리셋
|
||||||
|
* @param strength 모션 강도 (기본값: 0.1)
|
||||||
|
* @returns 계산된 벡터 (vectorA)
|
||||||
|
*/
|
||||||
|
export function presetToVector(preset: MotionPreset, strength: number = 0.1): Point {
|
||||||
|
switch (preset) {
|
||||||
|
case 'none':
|
||||||
|
// 애니메이션 없음
|
||||||
|
return {x: 0, y: 0};
|
||||||
|
|
||||||
|
case 'horizontal':
|
||||||
|
// 좌우 왕복
|
||||||
|
return {x: strength, y: 0};
|
||||||
|
|
||||||
|
case 'vertical':
|
||||||
|
// 상하 왕복
|
||||||
|
return {x: 0, y: strength};
|
||||||
|
|
||||||
|
case 'rotate-cw':
|
||||||
|
// 시계방향 회전 (원운동의 시작점)
|
||||||
|
return {x: strength, y: 0};
|
||||||
|
|
||||||
|
case 'rotate-ccw':
|
||||||
|
// 반시계방향 회전 (원운동의 시작점)
|
||||||
|
return {x: -strength, y: 0};
|
||||||
|
|
||||||
|
case 'pulse':
|
||||||
|
// 펄스 (중심에서 바깥으로)
|
||||||
|
return {x: strength, y: strength};
|
||||||
|
|
||||||
|
case 'diagonal-1':
|
||||||
|
// 대각선 (좌상→우하)
|
||||||
|
return {x: strength * 0.707, y: strength * 0.707}; // √2/2 ≈ 0.707
|
||||||
|
|
||||||
|
case 'diagonal-2':
|
||||||
|
// 대각선 (우상→좌하)
|
||||||
|
return {x: strength * 0.707, y: -strength * 0.707};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {x: 0, y: 0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 프리셋이 회전 타입인지 확인
|
||||||
|
*/
|
||||||
|
export function isRotationPreset(preset?: MotionPreset): boolean {
|
||||||
|
return preset === 'rotate-cw' || preset === 'rotate-ccw';
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user