MagicScroll
MagicScroll is a flexible collection of components intended to build various types of scroll-based animations and alerts.
<template>
<magic-scroll-provider
:target="parentRef"
class="bg-surface-base aspect-16/9 w-full"
>
<div ref="parent" class="relative h-full w-full overflow-auto">
<magic-scroll-scene
v-slot="{ progress }"
from="top-top"
to="bottom-bottom"
class="h-[500svh]"
>
<div
class="type-surface-body-sm text-surface-muted sticky top-0 mb-[-100%] flex w-full justify-between p-4"
>
<span>{{ Math.round(progress * 100) }}%</span>
<span>Scroll down</span>
</div>
<magic-scroll-motion
:sequence="sequence"
class="sticky top-0 flex aspect-16/9 w-full items-center justify-center"
>
<div class="bg-surface-high h-20 w-20" />
</magic-scroll-motion>
</magic-scroll-scene>
</div>
</magic-scroll-provider>
</template>
<script lang="ts" setup>
import { useTemplateRef } from 'vue'
import type { MagicScrollSequence } from '@maas/vue-equipment/plugins/MagicScroll'
const parentRef = useTemplateRef('parent')
const sequence: MagicScrollSequence = [
[
{
opacity: [0, 1],
},
],
[
{
x: ['0rem', '-5rem'],
y: ['0rem', '5rem'],
scale: [1, 1.25],
},
{ delay: 0.5 },
],
[
{
x: ['-5rem', '-5rem'],
y: ['5rem', '-5rem'],
},
{ delay: 0.5 },
],
[
{
x: ['-5rem', '5rem'],
y: ['-5rem', '-5rem'],
},
{ delay: 0.5 },
],
[
{
x: ['5rem', '5rem'],
y: ['-5rem', '5rem'],
},
{ delay: 0.5 },
],
[
{
x: ['5rem', '0rem'],
y: ['5rem', '0rem'],
scale: [1.25, 1],
},
{ delay: 0.5 },
],
]
</script>Installation
CLI
Add @maas/vue-equipment to your dependencies.
pnpm add @maas/vue-equipment
Vue
If you are using Vue, import and add MagicScrollPlugin to your app.
import { createApp } from 'vue'
import { MagicScrollPlugin } from '@maas/vue-equipment/plugins/MagicScroll'
const app = createApp({})
app.use(MagicScrollPlugin)
Nuxt
The components are available as a Nuxt module. In your Nuxt config file add @maas/vue-equipment/nuxt to your modules and add MagicScroll to the plugins in your configuration.
export default defineNuxtConfig({
modules: ['@maas/vue-equipment/nuxt'],
vueEquipment: {
plugins: ['MagicScroll'],
},
})
Peer Dependencies
MagicScroll relies on the following optional peer dependencies in addition to the core peer dependencies. Package managers don’t install optional peer dependencies automatically, so add them to your project.
Installation
pnpm add motion
API Reference
MagicScrollProvider
The MagicScrollProvider calculates the scroll distance for either the window or the given target and passes it down to its children.
Props
MagicScrollScene
The MagicScrollScene calculates the scroll progress based on the automatically injected scroll distance and the passed props.
For example from="top-bottom" to="bottom-top" would calculate the progress from where the top edge of the element reaches the bottom edge of the target until where the bottom edge of the element reaches the top edge of the target.
Data Attributes
MagicScrollMotion
MagicScrollMotion is a wrapper around motion.dev for scroll based animations.
Data Attributes
MagicScrollCollision
MagicScrollCollision emits an event once the element’s top or bottom edge collides with the target’s top or bottom edge.
Errors
Examples
Collision Detection
<template>
<magic-scroll-provider
:target="parentRef"
class="bg-surface-base aspect-square w-full"
>
<div ref="parentRef" class="relative h-full w-full overflow-auto">
<magic-scroll-scene
class="flex flex-col items-center justify-evenly gap-[100vh] pb-[100vh]"
>
<span
class="type-surface-body-sm text-surface-muted flex aspect-square w-full items-center justify-center"
>
Scroll down
</span>
<magic-scroll-collision
v-for="i in 4"
:id="`collision-${i}`"
:key="i"
class="bg-surface-high flex aspect-square w-full items-center justify-center"
>
<m-badge size="lg" variant="secondary">{{
`Collision ${i}`
}}</m-badge>
</magic-scroll-collision>
</magic-scroll-scene>
</div>
</magic-scroll-provider>
<magic-toast-provider
id="magic-scroll-collision-detection-demo"
:options="{ position: 'bottom-right' }"
/>
</template>
<script lang="ts" setup>
import { defineAsyncComponent, onBeforeUnmount, ref } from 'vue'
import { useMagicToast } from '@maas/vue-equipment/plugins/MagicToast'
import { useMagicEmitter } from '@maas/vue-equipment/plugins/MagicEmitter'
import { MBadge } from 'mirror-compat'
import type { MagicEmitterEvents } from '@maas/vue-equipment/plugins/MagicEmitter'
const parentRef = ref<HTMLElement | undefined>(undefined)
const component = defineAsyncComponent(
() => import('./components/CollisionDemoToast.vue')
)
const { add } = useMagicToast('magic-scroll-collision-detection-demo')
const emitter = useMagicEmitter()
function callback(payload: MagicEmitterEvents['collision']) {
add({ component, options: { duration: 5000 }, props: { payload } })
}
emitter.on('collision', callback)
onBeforeUnmount(() => {
emitter.off('collision', callback)
})
</script>