CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-svelte

Revolutionary JavaScript framework and compiler that builds web applications without runtime overhead by compiling components at build time.

Overview
Eval results
Files

list-animations.mddocs/

List Animations

FLIP animations for smooth list reordering and element positioning transitions using the First, Last, Invert, Play technique.

Capabilities

FLIP Animation

Create smooth transitions when elements change position in lists, providing natural motion for reordering, filtering, and layout changes.

/**
 * Creates FLIP (First, Last, Invert, Play) animation for element position changes
 * @param node - DOM element to animate
 * @param positions - Start and end DOMRect positions
 * @param params - Animation parameters
 * @returns Animation configuration
 */
function flip(
  node: Element, 
  { from, to }: { from: DOMRect; to: DOMRect }, 
  params?: FlipParams
): AnimationConfig;

interface FlipParams {
  /** Delay before animation starts (ms) */
  delay?: number;
  /** Animation duration (ms) or function based on distance */
  duration?: number | ((len: number) => number);
  /** Easing function */
  easing?: (t: number) => number;
}

interface AnimationConfig {
  delay?: number;
  duration?: number;
  easing?: (t: number) => number;
  css?: (t: number, u: number) => string;
  tick?: (t: number, u: number) => void;
}

Usage Examples:

// Basic list with FLIP animations
import { flip } from "svelte/animate";
import { cubicOut } from "svelte/easing";

let items = [
  { id: 1, name: "First item" },
  { id: 2, name: "Second item" },
  { id: 3, name: "Third item" }
];

function shuffle() {
  items = items.sort(() => Math.random() - 0.5);
}

// In template:
{#each items as item (item.id)}
  <div animate:flip={{ duration: 300 }}>
    {item.name}
  </div>
{/each}

// With custom parameters
{#each items as item (item.id)}
  <div animate:flip={{ 
    duration: d => Math.sqrt(d * 200),
    easing: cubicOut 
  }}>
    {item.name}
  </div>
{/each}

Dynamic Duration

Adjust animation duration based on the distance elements need to travel for more natural motion.

Usage Examples:

import { flip } from "svelte/animate";

// Duration proportional to distance
{#each todos as todo (todo.id)}
  <div animate:flip={{ duration: d => Math.sqrt(d * 150) }}>
    <input type="checkbox" bind:checked={todo.done} />
    {todo.text}
  </div>
{/each}

// Fixed duration for consistent timing
{#each items as item (item.id)}
  <div animate:flip={{ duration: 250 }}>
    {item.content}
  </div>
{/each}

Advanced List Patterns

Common patterns for using FLIP animations in interactive lists.

Sortable List:

import { flip } from "svelte/animate";
import { dndzone } from "svelte-dnd-action";

let items = [
  { id: 1, name: "Task 1" },
  { id: 2, name: "Task 2" },
  { id: 3, name: "Task 3" }
];

function handleDndConsider(e) {
  items = e.detail.items;
}

function handleDndFinalize(e) {
  items = e.detail.items;
}

// Template with drag-and-drop and FLIP
<div 
  use:dndzone={{ items }}
  on:consider={handleDndConsider}
  on:finalize={handleDndFinalize}
>
  {#each items as item (item.id)}
    <div animate:flip={{ duration: 300 }}>
      {item.name}
    </div>
  {/each}
</div>

Filtered List with Animations:

import { flip } from "svelte/animate";
import { scale } from "svelte/transition";

let items = [...]; // Your data
let filter = "";

$: filteredItems = items.filter(item => 
  item.name.toLowerCase().includes(filter.toLowerCase())
);

// Template
<input bind:value={filter} placeholder="Filter items..." />

{#each filteredItems as item (item.id)}
  <div 
    in:scale={{ duration: 200 }}
    out:scale={{ duration: 200 }}
    animate:flip={{ duration: 300 }}
  >
    {item.name}
  </div>
{/each}

Todo List with Categories:

import { flip } from "svelte/animate";
import { fly } from "svelte/transition";

let todos = [...];

function toggleDone(id) {
  todos = todos.map(todo => 
    todo.id === id ? { ...todo, done: !todo.done } : todo
  );
}

// Template
<div class="todo-sections">
  <section class="pending">
    <h2>Pending</h2>
    {#each todos.filter(t => !t.done) as todo (todo.id)}
      <div 
        animate:flip={{ duration: 300 }}
        in:fly={{ x: -100 }}
        out:fly={{ x: 100 }}
      >
        <input 
          type="checkbox" 
          checked={todo.done}
          on:change={() => toggleDone(todo.id)}
        />
        {todo.text}
      </div>
    {/each}
  </section>
  
  <section class="completed">
    <h2>Completed</h2>
    {#each todos.filter(t => t.done) as todo (todo.id)}
      <div 
        animate:flip={{ duration: 300 }}
        in:fly={{ x: -100 }}
        out:fly={{ x: 100 }}
      >
        <input 
          type="checkbox" 
          checked={todo.done}
          on:change={() => toggleDone(todo.id)}
        />
        <s>{todo.text}</s>
      </div>
    {/each}
  </section>
</div>

Performance Optimization

Tips for optimal FLIP animation performance:

import { flip } from "svelte/animate";

// Use shorter durations for better performance
{#each items as item (item.id)}
  <div animate:flip={{ duration: 200 }}>
    {item.name}
  </div>
{/each}

// Avoid complex easing functions for large lists
import { linear, cubicOut } from "svelte/easing";

{#each items as item (item.id)}
  <div animate:flip={{ 
    duration: 150,
    easing: items.length > 50 ? linear : cubicOut
  }}>
    {item.name}
  </div>
{/each}

// Consider disabling animations for very large lists
{#each items as item (item.id)}
  <div animate:flip={{ 
    duration: items.length > 100 ? 0 : 300
  }}>
    {item.name}
  </div>
{/each}

Integration with Transitions

Combine FLIP animations with enter/exit transitions for complete motion design:

import { flip } from "svelte/animate";
import { fade, fly, scale } from "svelte/transition";

// Fade in/out with position animations
{#each items as item (item.id)}
  <div 
    in:fade={{ duration: 200 }}
    out:fade={{ duration: 200 }}
    animate:flip={{ duration: 300 }}
  >
    {item.name}
  </div>
{/each}

// Slide in from side with repositioning
{#each items as item (item.id)}
  <div 
    in:fly={{ x: -50, duration: 200 }}
    out:fly={{ x: 50, duration: 200 }}
    animate:flip={{ duration: 300 }}
  >
    {item.name}
  </div>
{/each}

// Scale with smooth repositioning
{#each items as item (item.id)}
  <div 
    in:scale={{ start: 0.8, duration: 150 }}
    out:scale={{ start: 0.8, duration: 150 }}
    animate:flip={{ duration: d => Math.sqrt(d * 100) }}
  >
    {item.name}
  </div>
{/each}

FLIP Technique Explained

FLIP stands for:

  • First: Measure the initial position of elements
  • Last: Measure the final position after DOM changes
  • Invert: Apply a transform to move elements back to their starting positions
  • Play: Animate the transform back to 0, creating smooth motion

This technique allows for smooth animations even when the actual DOM changes happen instantly, making it perfect for list reordering, filtering, and layout changes.

Install with Tessl CLI

npx tessl i tessl/npm-svelte@4.2.0

docs

actions.md

compiler.md

core-runtime.md

easing-functions.md

index.md

list-animations.md

motion-animation.md

store-management.md

transitions.md

tile.json