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="w-full aspect-[16/9] bg-surface-elevation-base"
>
<div ref="parent" class="relative w-full h-full overflow-auto">
<magic-scroll-scene
v-slot="{ progress }"
from="top-top"
to="bottom-bottom"
class="h-[500svh]"
>
<div
class="sticky w-full top-0 p-4 mb-[-100%] type-surface-body-sm text-surface-subtle flex justify-between"
>
<span>{{ Math.round(progress * 100) }}%</span>
<span>Scroll down</span>
</div>
<magic-scroll-motion
:sequence="sequence"
class="sticky w-full aspect-[16/9] top-0 flex items-center justify-center"
>
<div class="w-20 h-20 bg-surface-elevation-high" />
</magic-scroll-motion>
</magic-scroll-scene>
</div>
</magic-scroll-provider>
</template>
<script lang="ts" setup>
import { useTemplateRef } from 'vue'
const parentRef = useTemplateRef('parent')
const sequence = [
[
{
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>
Overview
Installation
CLI
Add @maas/vue-equipment
to your dependencies.
pnpm install @maas/vue-equipment
npm install @maas/vue-equipment
yarn add @maas/vue-equipment
bun install @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'
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
If you haven’t installed the required peer dependencies automatically, you’ll need to install the following packages manually.
Installation
pnpm install @nuxt/kit @vueuse/core defu motion
npm install @nuxt/kit @vueuse/core defu motion
yarn add @nuxt/kit @vueuse/core defu motion
bun install @nuxt/kit @vueuse/core defu 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
Prop | Type | Required |
---|---|---|
false |
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.
Prop | Type | Required |
---|---|---|
false | ||
false |
MagicScrollMotion
MagicScrollMotion is a wrapper around motion.dev for scroll based animations.
Prop | Type | Required |
---|---|---|
true | ||
false | ||
number | false |
MagicScrollCollision
MagicScrollCollision emits an event once the element’s top or bottom edge collides with the target’s top or bottom edge.
Prop | Type | Required |
---|---|---|
string | false | |
false |
Examples
Collision Detection
<template>
<magic-scroll-provider
:target="parentRef"
class="bg-surface-elevation-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-subtle w-full aspect-square flex items-center justify-center"
>
Scroll down
</span>
<magic-scroll-collision
v-for="i in 4"
:id="`collision-${i}`"
:key="i"
class="bg-surface-elevation-high flex aspect-square w-full items-center justify-center"
>
<m-badge size="lg" mode="tone">{{ `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 { onBeforeUnmount, defineAsyncComponent, ref } from 'vue'
import {
useMagicToast,
useMagicEmitter,
type MagicEmitterEvents,
} from '@maas/vue-equipment/plugins'
import { MBadge } from '@maas/mirror/vue'
const parentRef = ref<HTMLElement | undefined>(undefined)
const component = defineAsyncComponent(
() => import('./components/CollisionToast.vue')
)
const { add } = useMagicToast('magic-scroll-collision-detection-demo')
function callback(payload: MagicEmitterEvents['collision']) {
add({ component, duration: 5000, props: { payload } })
}
useMagicEmitter().on('collision', callback)
onBeforeUnmount(() => {
useMagicEmitter().off('collision', callback)
})
</script>