vue-collision
v2.0.1
Published
fires a collide/non-collide event on collision with viewport or any other bounding box
Maintainers
Readme
vue-collision
Vue 3 directive that fires
collide/non-collideevents when an element enters or leaves the viewport, plus element-to-element group collision detection via AABB.
Features
- Viewport collision via
IntersectionObserver— zero-cost, edge-triggered - Element-to-element group collision via rAF + AABB overlap — groups any number of elements and checks every pair
- Fires native DOM
CustomEvents (collide,non-collide,collide-<group>,non-collide-<group>) - Grouping: place elements in named groups; any pair within a group is checked
.preventmodifier to opt out of viewport tracking- TypeScript source with
.d.tsdeclarations - Vue 3 only (v2.x); for Vue 2 use v1.x
Install
npm install vue-collisionQuick start
main.ts
import { createApp } from 'vue'
import VueCollision from 'vue-collision'
import App from './App.vue'
createApp(App)
.use(VueCollision, { globalTriggers: ['resize', 'scroll'] })
.mount('#app')Component (<script setup>)
<script setup lang="ts">
const onCollide = (e: CustomEvent) => {
// e.detail is `window` for viewport events
console.log('in viewport', e.detail)
}
const onLeave = (e: CustomEvent) => {
console.log('left viewport')
}
const onGroupCollide = (e: CustomEvent<HTMLElement>) => {
// e.detail is the other element this element collided with
console.log('collided with', e.detail)
}
</script>
<template>
<!-- tracks viewport + 'groupA' element collisions -->
<div
v-collision="['groupA']"
@collide="onCollide"
@non-collide="onLeave"
@collide-groupA="onGroupCollide"
@non-collide-groupA="onGroupCollide"
/>
<!-- viewport only (no custom group) -->
<div v-collision @collide="onCollide" @non-collide="onLeave" />
<!-- groupA only, skip viewport tracking -->
<div
v-collision.prevent="['groupA']"
@collide-groupA="onGroupCollide"
/>
</template>Directive reference
| Syntax | Behaviour |
|--------|-----------|
| v-collision | Viewport group + IntersectionObserver |
| v-collision.prevent | Opt out of viewport group |
| v-collision="['g1', 'g2']" | Add to named groups (g1, g2) |
| v-collision.prevent="['g1']" | Named groups only, no viewport tracking |
Events
All events are native DOM CustomEvents. Read the collider from event.detail.
| Event | Fires when | event.detail |
|-------|-----------|----------------|
| collide | Element enters the viewport | window |
| non-collide | Element leaves the viewport | window |
| collide-<group> | Element overlaps another in the same group | The other HTMLElement |
| non-collide-<group> | Element stops overlapping another in the same group | The other HTMLElement |
Plugin options
app.use(VueCollision, {
// Events that trigger element-group re-checks. Default: ['resize', 'scroll']
globalTriggers: ['resize', 'scroll'],
})Migrating from v1 (Vue 2 → Vue 3)
v2.0.0 is a full Vue 3 rewrite. Key breaking changes:
Install — replace
Vue.use(...)withapp.use(...).Event delivery — events are now native DOM
CustomEvents, not Vue component events. The collider is onevent.detail, not the first argument:// v1 (Vue 2) onCollide(collider) { /* collider was the Vue instance */ } // v2 (Vue 3) onCollide(event) { const collider = event.detail }Listen with
@on a plain element —v-collisionmust be placed on a host element (<div>,<section>, etc.) not a component root. If your component exposes its root element, usev-bind="$attrs"to forward the listeners.Viewport detection — now uses
IntersectionObserverinstead ofrequestAnimationFramepolling (lower CPU, fires immediately).Element-group re-check timing — element-group collision is checked once per frame-request, triggered by mount,
updated, and the configuredglobalTriggers(default:resize,scroll). Pure CSS transforms or animations that move elements without triggering a scroll, resize, or Vue reactive update will not automatically re-check. Add a global trigger event or callwindow.dispatchEvent(new Event('resize'))to force a re-check if needed.
Development
npm install
npm run typecheck
npm run build
npm testLicense
MIT
