refactor(helpers): modernize reactive helpers and add tests
This commit is contained in:
@@ -1,61 +1,83 @@
|
||||
/**
|
||||
* 3D perspective animation state manager
|
||||
*
|
||||
* Manages smooth transitions between "front" (interactive) and "back" (background)
|
||||
* visual states using Svelte springs. Used for creating depth-based UI effects
|
||||
* like settings panels, modal transitions, and spatial navigation.
|
||||
*
|
||||
* @example
|
||||
* ```svelte
|
||||
* <script lang="ts">
|
||||
* import { createPerspectiveManager } from '$shared/lib/helpers';
|
||||
*
|
||||
* const perspective = createPerspectiveManager({
|
||||
* depthStep: 100,
|
||||
* scaleStep: 0.5,
|
||||
* blurStep: 4
|
||||
* });
|
||||
* </script>
|
||||
*
|
||||
* <div
|
||||
* style="transform: scale({perspective.isBack ? 0.5 : 1});
|
||||
* filter: blur({perspective.isBack ? 4 : 0}px)"
|
||||
* >
|
||||
* <button on:click={perspective.toggle}>Toggle View</button>
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { Spring } from 'svelte/motion';
|
||||
|
||||
/**
|
||||
* Configuration options for perspective effects
|
||||
*/
|
||||
export interface PerspectiveConfig {
|
||||
/**
|
||||
* How many px to move back per level
|
||||
*/
|
||||
/** Z-axis translation per level in pixels */
|
||||
depthStep?: number;
|
||||
/**
|
||||
* Scale reduction per level
|
||||
*/
|
||||
/** Scale reduction per level (0-1) */
|
||||
scaleStep?: number;
|
||||
/**
|
||||
* Blur amount per level
|
||||
*/
|
||||
/** Blur amount per level in pixels */
|
||||
blurStep?: number;
|
||||
/**
|
||||
* Opacity reduction per level
|
||||
*/
|
||||
/** Opacity reduction per level (0-1) */
|
||||
opacityStep?: number;
|
||||
/**
|
||||
* Parallax intensity per level
|
||||
*/
|
||||
/** Parallax movement intensity per level */
|
||||
parallaxIntensity?: number;
|
||||
/**
|
||||
* Horizontal offset for each plan (x-axis positioning)
|
||||
* Positive = right, Negative = left
|
||||
*/
|
||||
/** Horizontal offset - positive for right, negative for left */
|
||||
horizontalOffset?: number;
|
||||
/**
|
||||
* Layout mode: 'center' (default) or 'split' for Swiss-style side-by-side
|
||||
*/
|
||||
/** Layout mode: 'center' for centered, 'split' for side-by-side */
|
||||
layoutMode?: 'center' | 'split';
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages perspective state with a simple boolean flag.
|
||||
* Manages perspective state with spring-based transitions
|
||||
*
|
||||
* Drastically simplified from the complex camera/index system.
|
||||
* Just manages whether content is in "back" or "front" state.
|
||||
* Simplified from a complex camera system to just track back/front state.
|
||||
* The spring value animates between 0 (front) and 1 (back) for smooth
|
||||
* visual transitions of scale, blur, opacity, and position.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* ```ts
|
||||
* const perspective = createPerspectiveManager({
|
||||
* depthStep: 100,
|
||||
* scaleStep: 0.5,
|
||||
* blurStep: 4,
|
||||
* blurStep: 4
|
||||
* });
|
||||
*
|
||||
* // Toggle back/front
|
||||
* perspective.toggle();
|
||||
* // Check state (reactive)
|
||||
* console.log(perspective.isBack); // false
|
||||
*
|
||||
* // Check state
|
||||
* const isBack = perspective.isBack; // reactive boolean
|
||||
* // Toggle with animation
|
||||
* perspective.toggle(); // Smoothly animates to back position
|
||||
*
|
||||
* // Direct control
|
||||
* perspective.setBack(); // Go to back
|
||||
* perspective.setFront(); // Go to front
|
||||
* ```
|
||||
*/
|
||||
export class PerspectiveManager {
|
||||
/**
|
||||
* Spring for smooth back/front transitions
|
||||
* Spring animation state
|
||||
* Animates between 0 (front) and 1 (back) with configurable physics
|
||||
*/
|
||||
spring = new Spring(0, {
|
||||
stiffness: 0.2,
|
||||
@@ -63,20 +85,30 @@ export class PerspectiveManager {
|
||||
});
|
||||
|
||||
/**
|
||||
* Reactive boolean: true when in back position (blurred, scaled down)
|
||||
* Reactive state: true when in back position
|
||||
*
|
||||
* Content should appear blurred, scaled down, and less interactive
|
||||
* when this is true. Derived from spring value > 0.5.
|
||||
*/
|
||||
isBack = $derived(this.spring.current > 0.5);
|
||||
|
||||
/**
|
||||
* Reactive boolean: true when in front position (fully visible, interactive)
|
||||
* Reactive state: true when in front position
|
||||
*
|
||||
* Content should be fully visible, sharp, and interactive
|
||||
* when this is true. Derived from spring value < 0.5.
|
||||
*/
|
||||
isFront = $derived(this.spring.current < 0.5);
|
||||
|
||||
/**
|
||||
* Configuration values for style computation
|
||||
* Internal configuration with defaults applied
|
||||
*/
|
||||
private config: Required<PerspectiveConfig>;
|
||||
|
||||
/**
|
||||
* Creates a new perspective manager
|
||||
* @param config - Configuration for visual effects
|
||||
*/
|
||||
constructor(config: PerspectiveConfig = {}) {
|
||||
this.config = {
|
||||
depthStep: config.depthStep ?? 100,
|
||||
@@ -90,8 +122,10 @@ export class PerspectiveManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle between front (0) and back (1) positions.
|
||||
* Smooth spring animation handles the transition.
|
||||
* Toggle between front and back positions
|
||||
*
|
||||
* Uses spring animation for smooth transition. Toggles based on
|
||||
* current state - if spring < 0.5 goes to 1, otherwise goes to 0.
|
||||
*/
|
||||
toggle = () => {
|
||||
const target = this.spring.current < 0.5 ? 1 : 0;
|
||||
@@ -99,31 +133,40 @@ export class PerspectiveManager {
|
||||
};
|
||||
|
||||
/**
|
||||
* Force to back position
|
||||
* Force to back position (blurred, scaled down)
|
||||
*/
|
||||
setBack = () => {
|
||||
this.spring.target = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Force to front position
|
||||
* Force to front position (fully visible, interactive)
|
||||
*/
|
||||
setFront = () => {
|
||||
this.spring.target = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get configuration for style computation
|
||||
* @internal
|
||||
* Get current configuration
|
||||
* @internal Used by components to compute styles
|
||||
*/
|
||||
getConfig = () => this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory function to create a PerspectiveManager instance.
|
||||
* Factory function to create a perspective manager
|
||||
*
|
||||
* @param config - Configuration options
|
||||
* @param config - Configuration options for visual effects
|
||||
* @returns Configured PerspectiveManager instance
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const perspective = createPerspectiveManager({
|
||||
* scaleStep: 0.6,
|
||||
* blurStep: 8,
|
||||
* layoutMode: 'split'
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function createPerspectiveManager(config: PerspectiveConfig = {}) {
|
||||
return new PerspectiveManager(config);
|
||||
|
||||
Reference in New Issue
Block a user