I just finished a working solution of a Vue 3 component to fully swipe away items. Being a noob, it took quite a bit of time to figure out as I couldn’t find a Vue 3 example or the examples were in-complete or weren’t using TypeScript. With that, I thought I’d share my code.
I primarily followed Building Interactive Ionic Apps with Gestures and Animations which is in Angular and referenced the Ionic docs - Gestures & Animations.
TailwindCSS is being used for the CSS.
Example
Code
GoalList.vue
<template>
<div>
<single-goal
v-for="goal in goals"
:goal="goal"
:key="goal.type + goal.category + goal.id"
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import SingleGoal from '@/components/SingleGoal.vue'
export default defineComponent({
name: 'GoalList',
components: {
SingleGoal,
},
data() {
return {
goals: [
{
id: 1,
type: 'daily',
category: 'cycling',
title: 'Cycle daily',
text: 'Cycle 15 miles today',
},
{
id: 2,
type: 'weekly',
category: 'running',
title: 'Run weekly',
text: 'Run 5 miles this week',
},
],
}
},
})
</script>
SingleGoal.vue
<template>
<ion-card class="my-1 grid" ref="cardRef">
<ion-item class="col-start-1 row-start-1 z-10" lines="none">
<div class="flex items-center py-2">
<div class="ml-2">
<h1 class="my-0 text-sm text-medium-shade">{{ goal.title }}</h1>
<p class="leading-5">{{ goal.text }}</p>
</div>
</div>
</ion-item>
<div class="grid col-start-1 row-start-1 items-center text-right text-medium-contrast pr-4">
Dismiss
</div>
</ion-card>
</template>
<style scoped>
ion-card {
--background: theme('colors.medium.shade');
}
</style>
<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue'
import { createAnimation, createGesture, IonItem, IonCard } from '@ionic/vue'
export default defineComponent({
name: 'SingleGoal',
props: {
goal: {
type: Object,
required: true,
},
},
components: {
IonCard,
IonItem,
},
setup() {
const cardRef = ref()
onMounted(() => {
const windowWidth = window.innerWidth
const itemElement = cardRef.value.$el.childNodes[0]
const deleteAnimation = createAnimation()
.addElement(cardRef.value.$el)
.duration(200)
.easing('ease-out')
.fromTo('height', '80px', 0)
const swipeGesture = createGesture({
el: itemElement,
threshold: 15,
direction: 'x',
gestureName: 'swipe-dismiss',
onMove: (ev) => {
// Reposition card
itemElement.style.transform = `translateX(${ev.deltaX}px)`
},
onEnd: (ev) => {
itemElement.style.transition = '0.2s ease-out'
// Fly out the card if we cross the threshold of 150px
if (ev.deltaX < -150) {
itemElement.style.transform = `translate3d(-${windowWidth}px, 0, 0)`
deleteAnimation.play()
deleteAnimation.onFinish(async () => {
cardRef.value.$el.style.display = 'none'
// Finish code to completely remove from your state
})
} else {
// Fly back to original position
itemElement.style.transform = ''
}
},
})
swipeGesture.enable()
})
return { cardRef }
},
})
</script>