Skip to content

Delta Indicators ​

Intermediate

Custom swipe indicators with dynamic opacity based on drag distance.

Demo ​

Source
vue
<script lang="ts" setup>
import { ref } from 'vue'
import { FlashCards } from 'vue3-flashcards'
import LanguageCard from './LanguageCard.vue'
import SwipeOverlay from './SwipeOverlay.vue'

interface WordCard {
  word: string
  translation: string
}

const cards = ref<WordCard[]>([
  { word: 'Hello', translation: 'Bonjour' },
  { word: 'World', translation: 'Monde' },
  { word: 'Thank you', translation: 'Merci' },
  { word: 'Goodbye', translation: 'Au revoir' },
  { word: 'Please', translation: 'S\'il vous plaît' },
])
</script>

<template>
  <div class="w-full flex justify-center items-center py-20">
    <div class="max-w-sm w-full">
      <FlashCards :items="cards">
        <template #default="{ item }">
          <LanguageCard :item="item" />
        </template>

        <template #approve="{ delta }">
          <SwipeOverlay :delta="Math.abs(delta)" type="approve" />
        </template>

        <template #reject="{ delta }">
          <SwipeOverlay :delta="Math.abs(delta)" type="reject" />
        </template>
      </FlashCards>
    </div>
  </div>
</template>
vue
<script setup lang="ts">
interface WordCard {
  word: string
  translation: string
}

defineProps<{
  item: WordCard
}>()
</script>

<template>
  <div class="relative overflow-hidden rounded-2xl shadow-xl h-48 bg-gradient-to-br from-indigo-500 to-purple-600 text-white transform transition-all duration-300">
    <!-- Language indicator -->
    <div class="absolute top-4 left-4 px-3 py-1 bg-white/20 rounded-full text-sm font-medium">
      EN → FR
    </div>

    <!-- Book icon -->
    <div class="absolute top-4 right-4 text-white/80">
      📚
    </div>

    <!-- Content -->
    <div class="p-6 h-full flex flex-col justify-center items-center gap-3 text-center">
      <div class="text-3xl font-bold mb-2">
        {{ item.word }}
      </div>
      <div class="text-xl text-indigo-100">
        {{ item.translation }}
      </div>
    </div>

    <!-- Swipe indicators -->
    <div class="absolute bottom-4 left-1/2 transform -translate-x-1/2 flex gap-2">
      <div class="w-2 h-2 bg-white/40 rounded-full" />
      <div class="w-2 h-2 bg-white/60 rounded-full" />
      <div class="w-2 h-2 bg-white rounded-full" />
    </div>
  </div>
</template>
vue
<script setup lang="ts">
defineProps<{
  delta: number
  type: 'approve' | 'reject'
}>()
</script>

<template>
  <div
    v-if="type === 'approve'"
    class="absolute inset-0 flex items-center justify-center bg-emerald-500/90 rounded-2xl font-bold text-2xl text-white backdrop-blur-sm"
    :style="{ opacity: delta }"
  >
    <div class="text-center">
      <div class="text-4xl mb-2">
        ✅
      </div>
      <div>I know this!</div>
    </div>
  </div>
  <div
    v-else
    class="absolute inset-0 flex items-center justify-center bg-red-500/90 rounded-2xl font-bold text-2xl text-white backdrop-blur-sm"
    :style="{ opacity: delta }"
  >
    <div class="text-center">
      <div class="text-4xl mb-2">
        📚
      </div>
      <div>Need to review</div>
    </div>
  </div>
</template>

Key Concepts ​

  • delta prop: Value from -1 to 1 indicating drag progress
  • Use for: Progressive opacity, scale, or color changes
  • Calculation: opacity = Math.abs(delta) for fade-in effect
  • Creates responsive visual feedback during drag

Released under the MIT License.