Skip to content

@vue-gsap-flip/core

The core package is the foundation of Vue Flip. It provides the essential functionality for creating flip animations in Vue 3 applications.

What's Included

The core package contains:

  • useFlip() - The main composable that manages flip animations
  • FlipElement - Vue component for easy integration
  • Plugin System - Extensible architecture for custom functionality
  • Type Definitions - Full TypeScript support

How It Works

Vue Flip works by leveraging GSAP's Flip plugin under the hood. Here's the basic flow:

  1. State Capture: Before any changes, Vue Flip captures the current state (position, size, etc.) of elements
  2. DOM Changes: Your Vue app makes changes to the DOM (reordering, showing/hiding, etc.)
  3. Animation: Vue Flip animates the elements from their old positions to their new positions
  4. Cleanup: The animation completes and cleanup occurs

Core Concepts

Flip Manager

The flip manager is the central orchestrator that:

  • Tracks elements that need to flip
  • Manages the animation lifecycle
  • Handles plugin integration
  • Provides configuration options

FlipElement Component

A Vue component that:

  • Wraps your content
  • Provides the setEl function for element reference
  • Handles the flip animation automatically
  • Supports configuration options

Plugin System

An extensible system that allows you to:

  • Add custom middleware
  • Modify animation behavior
  • Integrate with external libraries
  • Create reusable functionality

Basic Usage

Demo code
vue
<script setup>
import { FlipElement } from '@vue-gsap-flip/core'
import { ref } from 'vue'

const isExpanded = ref(false)

function toggleCard () {
  isExpanded.value = !isExpanded.value
}
</script>

<template>
  <div class="example">
    <button class="btn mb-2" @click="toggleCard">Toggle Card</button>
    <FlipElement
      id="card"
      :config="{
        flipVars: { duration: 0.3 },
        flipStateVars: { props: 'borderRadius,backgroundColor' }
      }"
      :trigger="isExpanded"
      v-slot="{ setEl }"
    >
      <div
        :ref="setEl"
        :class="{ 'card': !isExpanded, 'card--expanded': isExpanded }"
      />
    </FlipElement>
  </div>
</template>

<style scoped>
.example {
  width: 100%;
}

.card {
  background-color: white;
  border-radius: 8px;
  width: 100px;
  height: 100px;
}

.card--expanded {
  border-radius: 200px;
  margin-left: 50%;
  height: 200px;
  width: 50%;
  background-color: red;
}
</style>

Advanced Usage

For more control, you can use the useFlip composable directly:

vue
<script setup>
import { useFlip } from '@vue-gsap-flip/core'

const flipManager = useFlip()

// Trigger a flip animation
async function triggerFlip () {
  flipManager.detach('CUSTOM-ID', elementRef, { clone: true })
  // Make DOM changes
  // if you want you can animate animate the clone element
  // for example during page transitions
  const data = store.get('CUSTOM-ID')
  await gsap.to(data.clone, { scale: 2 })
  store.set('flip-img', { ...data, state: Flip.getState(data.clone as HTMLElement) })
  // flip to the current position
  flipManager.attach('CUSTOM-ID', elementRef)
}
</script>

A real example can be during a vue transition (or a page transition), where we can move the clone during the transition.

Step 1

Demo code
vue
<script setup lang="ts">
import { FlipElement, useFlip } from '@vue-gsap-flip/core'
import { gsap } from 'gsap'
import { Flip } from 'gsap/Flip'
import { ref } from 'vue'

const manager = useFlip()
const toggle = ref(false)
const transitioning = ref(false)
const config = { clone: true, flipStateVars: { scale: true } }

function onLeave (el: Element, done: () => void) {
  const tl = gsap.timeline({ onComplete: () => done() })
  tl.to(gsap.utils.toArray('.box-text', el), { autoAlpha: 0, duration: 0.5 })
  const data = manager.store.get('box')
  if (data?.clone) {
    tl.to(data.clone, {
      rotate: 360,
      x: window.innerWidth / 2,
      xPercent: -50,
      yPercent: -150,
      duration: 0.5,
      onComplete: () => {
        manager.store.set('box', { ...data, state: Flip.getState(data.clone as HTMLElement, data.config.flipStateVars) })
      }
    })
  }
}

function onEnter (el: Element, done: () => void) {
  const tl = gsap.timeline({ onComplete: () => done() })
  tl.from(gsap.utils.toArray('.box-text', el), { autoAlpha: 0, duration: 0.5 })
}
</script>

<template>
  <div class="w-full">
    <button class="btn mb-2" :disabled="transitioning" @click="toggle = !toggle">Toggle</button>
    <div class="border-solid border-blue">
      <Transition
        mode="out-in"
        :css="false"
        @after-enter="transitioning = false"
        @before-leave="transitioning = true"
        @enter="onEnter"
        @leave="onLeave"
      >
        <div v-if="!toggle" class="grid grid-cols-2">
          <div class="box box-text">
            <p>Step 1</p>
          </div>
          <div class="box">
            <FlipElement id="box" :config="config" v-slot="{ setEl }">
              <div :ref="setEl" class="w-4 h-4 bg-blue" />
            </FlipElement>
          </div>
        </div>
        <div v-else class="grid grid-cols-2">
          <div class="box">
            <FlipElement id="box" :config="config" v-slot="{ setEl }">
              <div :ref="setEl" class="w-30 h-30 bg-blue" />
            </FlipElement>
          </div>
          <div class="box box-text">
            <p>Step 2</p>
          </div>
        </div>
      </Transition>
    </div>
  </div>
</template>

<style scoped>
.box {
  @apply h-40 p-4 flex items-center justify-center;
}
</style>

Next Steps

Ready to dive deeper? Start with the Flip Manager to understand the core concepts.

Released under the MIT License.