Long press gesture? vue3

Any ionic master with an example on how to create the long press gesture? Latest info I found is 3 years old with references to articles that don’t exist no more. :frowning:

made a directive if anyone needs

// v-long-press.js
export default {
    beforeMount(el, binding) {
        // Define start and cancel functions as properties on the element
        el._pressTimer = null

        el._start = (e) => {
            if (el._pressTimer === null) {
                el._pressTimer = setTimeout(() => {
                    binding.value(e)
                    clearTimeout(el._pressTimer)
                    el._pressTimer = null
                }, 600)
            }
        }

        el._cancel = () => {
            if (el._pressTimer !== null) {
                clearTimeout(el._pressTimer)
                el._pressTimer = null
                el.isOpen = false
            }
        }

        // Add event listeners for touch events
        el.addEventListener("touchstart", el._start)
        el.addEventListener("touchend", el._cancel)
        el.addEventListener("touchcancel", el._cancel)
    },

    unmounted(el) {
        // Remove event listeners using the functions stored on the element
        el.removeEventListener("touchstart", el._start)
        el.removeEventListener("touchend", el._cancel)
        el.removeEventListener("touchcancel", el._cancel)

        // Clean up references
        delete el._start
        delete el._cancel
        delete el._pressTimer
    },
}

in component script

import vLongPress from "./v-long-press"
...

directives: {
        longPress: vLongPress,
    },

in component template

<ion-button v-long-press="handleClickActionSheet" color="clear">
            {{ currentMealType.name }}
</ion-button>

NOTE: works only on phone, in webview (chrome console in my case) the touch events intervene with native scrolling, not sure how, but deployed on the phone it works as expected

1 Like

also the first one I shared will get triggered on swipe if swipe lasts over 600ms … so if you have ion-sliding-item and you want to use the v-long-press on some element there … use this directive

// v-long-press.js
export default {
    beforeMount(el, binding) {
        // Initialize variables on the element to track state
        el._pressTimer = null
        el._startX = 0
        el._startY = 0
        el._moved = false

        // Start function to initiate long press detection
        el._start = (e) => {
            el._moved = false // Reset move flag

            if (e.type === "touchstart") {
                // Track initial touch position
                el._startX = e.touches[0].clientX
                el._startY = e.touches[0].clientY
            }

            // Set a timer for the long press
            if (el._pressTimer === null) {
                el._pressTimer = setTimeout(() => {
                    if (!el._moved && typeof binding.value === "function") {
                        binding.value(e)
                    }
                    clearTimeout(el._pressTimer)
                    el._pressTimer = null
                }, 600)
            }
        }

        // Move function to detect swipe or drag
        el._move = (e) => {
            const currentX = e.touches[0].clientX
            const currentY = e.touches[0].clientY

            // Check if the touch has moved significantly (swipe/scroll)
            if (Math.abs(currentX - el._startX) > 10 || Math.abs(currentY - el._startY) > 10) {
                el._moved = true // Mark as moved
                el._cancel() // Cancel the long press
            }
        }

        // Cancel function to clear timer and reset state
        el._cancel = () => {
            if (el._pressTimer !== null) {
                clearTimeout(el._pressTimer)
                el._pressTimer = null
            }
        }

        // Add event listeners
        el.addEventListener("touchstart", el._start)
        el.addEventListener("touchmove", el._move)
        el.addEventListener("touchend", el._cancel)
        el.addEventListener("touchcancel", el._cancel)
    },

    unmounted(el) {
        // Clean up event listeners on unmount
        el.removeEventListener("touchstart", el._start)
        el.removeEventListener("touchmove", el._move)
        el.removeEventListener("touchend", el._cancel)
        el.removeEventListener("touchcancel", el._cancel)

        // Clean up properties
        delete el._start
        delete el._move
        delete el._cancel
        delete el._pressTimer
        delete el._startX
        delete el._startY
        delete el._moved
    },
}

I just used onLongPress | VueUse :smile:

I actually knew about it I swear :smiley: but I would install the package solely just for this directive which felt excessive. If I bump into a scenario where i need some other vueuse stuff I will get the package and just replace this, for for now if i need just the long press I’ll choose my 2KB version :stuck_out_tongue:

:sweat_smile: I had the same thought, but with tree shaking the addition should be tiny. I went with it so as to not reinvent the wheel.

ou I didn’t know that, do I have to set up something special for tree shaking to occur or does it happen on build by itself? Because of course if I can just use something well maintained that’s always better, my decision was only size based

Tree shaking should just work out of the box if you are using standard Vue bundling (Vite currently). All of Ionic’s components are also “shook”.

1 Like

Wouldn’t it make sense to call the haptics api to give feedback to the user?

1 Like

oh of course! completely forgot about this, that’s awesome, thank you!