🌊 A shared layout animations for vue like framer motion, use layoutId
prop and components will animate from one to another.
🏎 Smooth animations based on @vueuse/motion
✨ Written in TypeScript
🙌 Easy to use
✅ Support most of the @vueuse/motion's props
npm install hero-motion
Suggest to use this component wrap your hole project, this provider will provide private context for Hero
component.
<script setup>
import { HeroProvider } from 'hero-motion'
</script>
<template>
<HeroProvider>
<RouterView />
</HeroProvider>
</template>
You can use hero with both component and composable method.
Component
<script setup>
import { Hero } from 'hero-motion'
</script>
<template>
<Hero as="div" layout-id="ID" />
</template>
Composable
<script setup>
import { ref } from 'vue'
import { useHero } from 'hero-motion'
const domRef = ref()
useHero(domRef, {
layoutId: 'LAYOUT_ID'
})
</script>
<template>
<div ref="domRef" />
</template>
See playground.
hero-motion
is typically used to optimize code structure
For better understanding, recommend exec npm run play:vite
in the root directory of this project.
Here are two common examples:
There is a Tabs Component in playgound vite example, and let's pay attention to the TabCursor
This is the slider cursor for each Tab
under the Tabs
component.
<script setup lang="ts">
import { Hero } from 'hero-motion'
</script>
<template>
<Hero layout-id="tab-cursor" as="span" class="absolute z-0 inset-0 rounded-lg bg-white dark:bg-default shadow-small">
<slot />
</Hero>
</template>
Typically, to ensure the slider cursor is positioned on the currently active tab, TabCursor
component should be placed directly under the Tabs
component with multiple Tab
s sharing a single TabCursor
.
With hero-motion
, each Tab
can define its own slider cursor. This approach not only eliminates the need to manage specific position information but also leads to better code organization.
Let’s take a look at the content of the Tab
, and this will show us how to control the display of the slider cursor using only v-if
.
While also having transition animations.
<script setup lang="ts">
import TabCursor from './TabCursor.vue'
defineProps<{
isActive: boolean
}>()
const emit = defineEmits(['click'])
</script>
<template>
<button @click="emit('click')">
<div>
<slot />
</div>
<TabCursor v-if="isActive" />
</button>
</template>
hero-motion
support tansition across different pages, even when components are under different routes, ensure smooth transitions when navigating between them.
See the Avatar component in the playground:
<script setup lang="ts">
import { Hero } from 'hero-motion'
defineProps<{
id: number | string
size?: number | string
}>()
</script>
<template>
<Hero :layout-id="`avatar:${id}`" :style="{ width: `${size}px`, height: `${size}px` }">
<img>
</Hero>
</template>
We can see that the Avatar
component is used in both the list and detail pages, and there will be a transition effect between avatars with the same ID.
hero-motion
will automatically handle transitions between positions and sizes.
- Type:
string
- Default:
'div'
- Type:
string | number
- Default:
undefined
- Required:
true
- Type:
string[]
- Default:
[]
- Type:
Transition
- Default:
undefined
Transition props can be used in both HeroProvider
and Hero
.
The configuration defined in HeroProvider
will be used as global default value, and you do not need to re-declare it on each Hero
components.
interface Transition {
delay: number
repeat: number
repeatDelay: number
repeatType: 'loop' | 'mirror' | 'reverse'
type: 'spring' | 'keyframes'
stiffness: number
damping: number
mass: number
bounce: number
duration: number
ease: string
}
Example:
<Hero
as="div"
layout-id="cursor"
:ignore="['width']"
:transition="{
duration: 1000,
type: 'keyframes',
bounce: 0
}"
/>
- Type:
() => void
- Default:
() => {}