Ionic 5 & Vue 3 Swipe Example with TypeScript

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

vue3_swipe

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>
3 Likes