W↓
All docs
🔑
Sign Up/Sign In
motion.dev/docs/vue-
Public Link
Apr 8, 2025, 12:35:39 PM - complete - 317.2 kB
Starting URLs:
https://motion.dev/docs/vue
CSS Selector:
article
Crawl Prefixes:
https://motion.dev/docs/vue-
## Page: https://motion.dev/docs/vue # Get started with Motion for Vue Motion for Vue is a simple yet limitless animation library. It's the only animation library with a **hybrid engine**, capable of hardware accelerated animations. In this guide, we'll learn how to install Motion Vue and take a whirlwind tour of its main features. ## Install Motion is available via npm: npm install motion\-v ### Nuxt modules Motion Vue offers Nuxt modules support. In `nuxt.config.ts`, simply add `motion-v/nuxt` into the modules, and it will auto-imports all the components for you. export default defineNuxtConfig({ modules: \['motion-v/nuxt'\], }) ### `unplugin-vue-components` Motion for Vue also supports unplugin-vue-components to auto-import all components from `motion-v`: import Components from 'unplugin-vue-components/vite' import MotionResolver from 'motion-v/resolver' export default defineConfig({ plugins: \[ vue(), Components({ dts: true, resolvers: \[ MotionResolver() \], }), \], }) > **Note:** Auto-import currently doesn't support the <motion /> component so you'll need to import it manually. **Note:** Motion for Vue contains APIs specifically tailored for Vue, but every feature from vanilla Motion is also compatible and available for advanced use-cases. ## Usage The core of Motion for Vue is the `<motion />` component. It's a normal DOM element, supercharged with animation capabilities. <template\> <motion.div /> </template\> Animating a `motion` component is as straightforward as setting values via the `animate` prop: <motion.ul :animate\="{ rotate: 360 }" /> When values in `animate` change, the component will animate. Motion has great-feeling defaults, but animations can of course be configured via the `transition` prop. <motion.div :animate\="{ scale: 2, transition: { duration: 2 } }" /> ### Enter animation When a component enters the page, it will automatically animate from the rendered value, but you can provide different values via the `initial` prop. <motion.button :initial\="{ scale: 0 }" :animate\="{ scale: 1 }" /> Or disable this initial animation entirely by setting `initial` to `false`. <motion.button :initial\="false" :animate\="{ scale: 1 }" /> ### Gestures `<motion />` extends Vue's event system with powerful gesture recognises. It currently supports hover, press, focus, and drag gestures. <motion.button :whileHover\="{ scale: 1.1 }" :whilePress\="{ scale: 0.95 }" @hoverStart\="() \=> console.log('hover started!')" /> Motion's gestures are designed to feel better than using CSS alone. For instance, hover events are correctly not triggered by touch screen taps. Learn more about gestures. ### Scroll animations Motion supports both types of scroll animations, **scroll-triggered** and **scroll-linked**. To trigger an animation on scroll, the `whileInView` prop defines a state to animate to/from when an element enters/leaves the viewport: <motion.div :initial\="{ backgroundColor: 'rgb(0, 255, 0)', opacity: 0 }" :whileInView\="{ backgroundColor: 'rgb(255, 0, 0)', opacity: 1 }" /> Whereas to link a value directly to scroll position, it's possible to use `MotionValue`s via `useScroll`. <script setup \> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> Learn more about Motion's scroll animations. ### Layout animations Motion has an industry-leading layout animation engine that supports animating between changes in layout, using only transforms, between the same or different elements, with full scale correction. It's as easy as applying the `layout` prop. <motion.div layout /> Or to animate between different elements, a `layoutId`: <motion.div layoutId\="underline" /> Learn more about layout animations. ### Exit animations By wrapping `motion` components with `<AnimatePresence>` we gain access to the `exit` prop. <AnimatePresence\> <motion.div v-if\="show" key\="box" :exit\="{ opacity: 0 }" /> </AnimatePresence\> Learn more about `AnimatePresence`. ## Learn next That's a very quick overview of Motion for Vue's basic features. But there's a lot more to learn! Next, we recommend diving further into the the `<motion />` component to learn more about its powerful features, like variants. Or, you can dive straight in to our examples, where each example comes complete with full source code that you can copy/paste into your project. Motion for Vue is a simple yet limitless animation library. It's the only animation library with a **hybrid engine**, capable of hardware accelerated animations. In this guide, we'll learn how to install Motion Vue and take a whirlwind tour of its main features. ## Install Motion is available via npm: npm install motion\-v ### Nuxt modules Motion Vue offers Nuxt modules support. In `nuxt.config.ts`, simply add `motion-v/nuxt` into the modules, and it will auto-imports all the components for you. export default defineNuxtConfig({ modules: \['motion-v/nuxt'\], }) ### `unplugin-vue-components` Motion for Vue also supports unplugin-vue-components to auto-import all components from `motion-v`: import Components from 'unplugin-vue-components/vite' import MotionResolver from 'motion-v/resolver' export default defineConfig({ plugins: \[ vue(), Components({ dts: true, resolvers: \[ MotionResolver() \], }), \], }) > **Note:** Auto-import currently doesn't support the <motion /> component so you'll need to import it manually. **Note:** Motion for Vue contains APIs specifically tailored for Vue, but every feature from vanilla Motion is also compatible and available for advanced use-cases. ## Usage The core of Motion for Vue is the `<motion />` component. It's a normal DOM element, supercharged with animation capabilities. <template\> <motion.div /> </template\> Animating a `motion` component is as straightforward as setting values via the `animate` prop: <motion.ul :animate\="{ rotate: 360 }" /> When values in `animate` change, the component will animate. Motion has great-feeling defaults, but animations can of course be configured via the `transition` prop. <motion.div :animate\="{ scale: 2, transition: { duration: 2 } }" /> ### Enter animation When a component enters the page, it will automatically animate from the rendered value, but you can provide different values via the `initial` prop. <motion.button :initial\="{ scale: 0 }" :animate\="{ scale: 1 }" /> Or disable this initial animation entirely by setting `initial` to `false`. <motion.button :initial\="false" :animate\="{ scale: 1 }" /> ### Gestures `<motion />` extends Vue's event system with powerful gesture recognises. It currently supports hover, press, focus, and drag gestures. <motion.button :whileHover\="{ scale: 1.1 }" :whilePress\="{ scale: 0.95 }" @hoverStart\="() \=> console.log('hover started!')" /> Motion's gestures are designed to feel better than using CSS alone. For instance, hover events are correctly not triggered by touch screen taps. Learn more about gestures. ### Scroll animations Motion supports both types of scroll animations, **scroll-triggered** and **scroll-linked**. To trigger an animation on scroll, the `whileInView` prop defines a state to animate to/from when an element enters/leaves the viewport: <motion.div :initial\="{ backgroundColor: 'rgb(0, 255, 0)', opacity: 0 }" :whileInView\="{ backgroundColor: 'rgb(255, 0, 0)', opacity: 1 }" /> Whereas to link a value directly to scroll position, it's possible to use `MotionValue`s via `useScroll`. <script setup \> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> Learn more about Motion's scroll animations. ### Layout animations Motion has an industry-leading layout animation engine that supports animating between changes in layout, using only transforms, between the same or different elements, with full scale correction. It's as easy as applying the `layout` prop. <motion.div layout /> Or to animate between different elements, a `layoutId`: <motion.div layoutId\="underline" /> Learn more about layout animations. ### Exit animations By wrapping `motion` components with `<AnimatePresence>` we gain access to the `exit` prop. <AnimatePresence\> <motion.div v-if\="show" key\="box" :exit\="{ opacity: 0 }" /> </AnimatePresence\> Learn more about `AnimatePresence`. ## Learn next That's a very quick overview of Motion for Vue's basic features. But there's a lot more to learn! Next, we recommend diving further into the the `<motion />` component to learn more about its powerful features, like variants. Or, you can dive straight in to our examples, where each example comes complete with full source code that you can copy/paste into your project. Motion for Vue is a simple yet limitless animation library. It's the only animation library with a **hybrid engine**, capable of hardware accelerated animations. In this guide, we'll learn how to install Motion Vue and take a whirlwind tour of its main features. ## Install Motion is available via npm: npm install motion\-v ### Nuxt modules Motion Vue offers Nuxt modules support. In `nuxt.config.ts`, simply add `motion-v/nuxt` into the modules, and it will auto-imports all the components for you. export default defineNuxtConfig({ modules: \['motion-v/nuxt'\], }) ### `unplugin-vue-components` Motion for Vue also supports unplugin-vue-components to auto-import all components from `motion-v`: import Components from 'unplugin-vue-components/vite' import MotionResolver from 'motion-v/resolver' export default defineConfig({ plugins: \[ vue(), Components({ dts: true, resolvers: \[ MotionResolver() \], }), \], }) > **Note:** Auto-import currently doesn't support the <motion /> component so you'll need to import it manually. **Note:** Motion for Vue contains APIs specifically tailored for Vue, but every feature from vanilla Motion is also compatible and available for advanced use-cases. ## Usage The core of Motion for Vue is the `<motion />` component. It's a normal DOM element, supercharged with animation capabilities. <template\> <motion.div /> </template\> Animating a `motion` component is as straightforward as setting values via the `animate` prop: <motion.ul :animate\="{ rotate: 360 }" /> When values in `animate` change, the component will animate. Motion has great-feeling defaults, but animations can of course be configured via the `transition` prop. <motion.div :animate\="{ scale: 2, transition: { duration: 2 } }" /> ### Enter animation When a component enters the page, it will automatically animate from the rendered value, but you can provide different values via the `initial` prop. <motion.button :initial\="{ scale: 0 }" :animate\="{ scale: 1 }" /> Or disable this initial animation entirely by setting `initial` to `false`. <motion.button :initial\="false" :animate\="{ scale: 1 }" /> ### Gestures `<motion />` extends Vue's event system with powerful gesture recognises. It currently supports hover, press, focus, and drag gestures. <motion.button :whileHover\="{ scale: 1.1 }" :whilePress\="{ scale: 0.95 }" @hoverStart\="() \=> console.log('hover started!')" /> Motion's gestures are designed to feel better than using CSS alone. For instance, hover events are correctly not triggered by touch screen taps. Learn more about gestures. ### Scroll animations Motion supports both types of scroll animations, **scroll-triggered** and **scroll-linked**. To trigger an animation on scroll, the `whileInView` prop defines a state to animate to/from when an element enters/leaves the viewport: <motion.div :initial\="{ backgroundColor: 'rgb(0, 255, 0)', opacity: 0 }" :whileInView\="{ backgroundColor: 'rgb(255, 0, 0)', opacity: 1 }" /> Whereas to link a value directly to scroll position, it's possible to use `MotionValue`s via `useScroll`. <script setup \> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> Learn more about Motion's scroll animations. ### Layout animations Motion has an industry-leading layout animation engine that supports animating between changes in layout, using only transforms, between the same or different elements, with full scale correction. It's as easy as applying the `layout` prop. <motion.div layout /> Or to animate between different elements, a `layoutId`: <motion.div layoutId\="underline" /> Learn more about layout animations. ### Exit animations By wrapping `motion` components with `<AnimatePresence>` we gain access to the `exit` prop. <AnimatePresence\> <motion.div v-if\="show" key\="box" :exit\="{ opacity: 0 }" /> </AnimatePresence\> Learn more about `AnimatePresence`. ## Learn next That's a very quick overview of Motion for Vue's basic features. But there's a lot more to learn! Next, we recommend diving further into the the `<motion />` component to learn more about its powerful features, like variants. Or, you can dive straight in to our examples, where each example comes complete with full source code that you can copy/paste into your project. --- ## Page: https://motion.dev/docs/vue-animation # Animation Motion for Vue offers a number of ways to animate your UI. Scaling from extremely simple prop-based animations, to more complex orchestration. ## Basic animations You'll perform almost all animations on a `<motion />` component. This is basically a DOM element with motion superpowers. import { motion } from "motion-v" For basic animations, you can update values on the `animate` prop: <motion.div :animate\="{ opacity: 1 }" /> When any value in its animate prop changes, the component will automatically animate to the new target. ## Animatable values Motion can animate any CSS value, even those that can't be animated by browsers, like `mask-image`. It supports: * Numbers: `0`, `100` etc. * Strings containing numbers: `"0vh"`, `"10px"` etc. * Colors: Hex, RGBA, HSLA. * Complex strings containing multiple numbers and/or colors (like `box-shadow`). * `display: "none"/"block"` and `visibility: "hidden"/"visible"`. ### Value type conversion In general, values can only be animated between two of the same type (i.e `"0px"` to `"100px"`). Colors can be freely animated between hex, RGBA and HSLA types. Additionally, `x`, `y`, `width`, `height`, `top`, `left`, `right` and `bottom` can animate between different value types. <motion.div :initial\='{ x: "100%" }' :animate\='{ x: "calc(100vw - 50%)" }' /> It's also possible to animate `width` and `height` in to/out of `"auto"`. <motion.div :initial\='{ height: "0px" }' :animate\='{ height: "auto" }' /> **Note:** If additionally animating `display` in to/out of `"none"`, replace this with `visibility` `"hidden"` as elements with `display: none` can't be measured. ### Transforms Unlike CSS, Motion can animate every transform axis independently: * Translate: `x`, `y`, `z` * Scale: `scale`, `scaleX`, `scaleY` * Rotate: `rotate`, `rotateX`, `rotateY`, `rotateZ` * Skew: `skew`, `skewX`, `skewY` * Perspective: `transformPerspective` `motion` components have enhanced `style` props, allowing you to set individual transforms: <motion.section :style\="{ x: -20 }" /> Animating transforms independently provides great flexibility, especially around gestures. <motion.button :whileHover\="{ scale: 1.1 }" whilePress\="{ scale: 0.9 }" /> Independent transforms perform great, but Motion's hybrid engine also uniquely offers hardware acceleration by setting `transform` directly. <motion.li :initial\='{ transform: "translateX(-100px)" }' :animate\='{ transform: "translateX(0px)" }' :transition\='{ type: "spring" }' /> **SVG note:** For SVG components, `x` and `y` **attributes** can be set using `attrX` and `attrY`. ### Transform origin `transform-origin` has three shortcut values that can be set and animated individually: * `originX` * `originY` * `originZ` If set as numbers, `originX` and `Y` default to a progress value between `0` and `1`. `originZ` defaults to pixels. <motion.div :style\='{ originX: 0.5 }' /> ### CSS variables Motion for Vue can animate the value of CSS variables, and also use CSS variables as animation targets. #### Animating CSS variables Sometimes it's convenient to be able to animate a CSS variable to animate many children: <motion.ul :initial\="{ '--rotate': '0deg' }" :animate\="{ '--rotate': '360deg' }" :transition\="{ duration: 2, repeat: Infinity }" \> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> </motion.ul\> **Note:** Animating the value of a CSS variable **always triggers paint**, therefore it can be more performant to use `MotionValue`s to setup this kind of animation. ### CSS variables as animation targets HTML `motion` components accept animation targets with CSS variables: <motion.li :animate\="{ backgroundColor: 'var(--action-bg)' }" /> #### SVG line drawing Line drawing animations can be created with many different SVG elements using three special properties: `pathLength`, `pathSpacing` and `pathOffset`. <motion.path :initial\="{ pathLength: 0 }" :animate\="{ pathLength: 1 }" /> All three are set as a progress value between `0` and `1`, `1` representing the total length of the path. Path animations are compatible with `circle`, `ellipse`, `line`, `path`, `polygon`, `polyline` and `rect` elements. ## Transitions By default, Motion will create appropriate transitions for snappy animations based on the type of value being animated. For instance, physical properties like `x` or `scale` are animated with spring physics, whereas values like `opacity` or `color` are animated with duration-based easing curves. However, you can define your own animations via the `transition` prop. <motion.div :animate\="{ x: 100 }" :transition\="{ ease: 'easeOut', duration: 2 }" /> ## Enter animations When a `motion` component is first created, it'll automatically animate to the values in `animate` if they're different from those initially rendered, which you can either do via CSS or via the `initial` prop. <motion.li :initial\="{ opacity: 0, scale: 0 }" :animate\="{ opacity: 1, scale: 1 }" /> You can also disable the enter animation entirely by setting `:initial="false"`. This will make the element render with the values defined in `animate`. <motion.div :initial\="false" :animate\="{ y: 100 }" /> ## Exit animations You can also easily animate elements as they exit the DOM. In Vue, when a component is removed, it's usually removed instantly. Motion provides the `AnimatePresence` component which keeps elements in the DOM while they perform an `exit` animation. <AnimatePresence\> <motion.div v-if\="isVisible" key\="modal" :initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :exit="{ opacity: 0 }" /> </AnimatePresence\> ## Keyframes Values in `animate` can be set as a series of keyframes. This will animate through each value in sequence. <motion.div :animate\="{ x: \[0, 100, 0\] }" /> We can use a value's current state as the initial keyframe by setting it to `null`. <motion.div :animate\="{ x: \[null, 100, 0\] }" /> This way, if a keyframe animation is interrupting another animation, the transition will feel more natural. By default, each keyframe is spaced naturally throughout the animation. You can override this by setting the `times` option via `transition`. `times` is an array of progress values between `0` and `1`, defining where in the animation each keyframe should be positioned. <motion.circle :cx\="500" :animate\="{ cx: \[null, 100, 200\], transition={{ duration: 3, times: \[0, 0.2, 1\] }} }" /> ## Gesture animations Motion for Vue has shortcut props for animating to/from a target when a gesture starts/ends. <motion.button :initial\="{ opacity: 0 }" :whileHover\="{ backgroundColor: 'rgba(220, 220, 220, 1)' }" :whilePress\="{ backgroundColor: 'rgba(255, 255, 255, 1)' }" :whileInView\="{ opacity: 1 }" /> It supports `hover`, `press`, `drag`, `focus` and `inView`. ## Variants Setting `animate` as a target is useful for simple, single-element animations. But sometimes we want to orchestrate animations that propagate throughout the DOM. We can do so with variants. Variants are a set of named targets. const variants = { visible: { opacity: 1 }, hidden: { opacity: 0 }, } They're passed to `motion` components via the `variants` prop: <motion.div :variants\="variants" /> These variants can now be referred to by a label, wherever you can define an animation target: <motion.div :variants\="variants" initial\="hidden" whileInView\="visible" /> You can also define multiple variants via an array: :animate\="\['visible', 'danger'\]" ### Propagation This is already useful for reusing and combining animation targets. But it becomes powerful for orchestrating animations throughout trees. Variants will flow down through `motion` components. So in this example when the `ul` enters the viewport, all of its children with a "visible" variant will also animate in: const list = { visible: { opacity: 1 }, hidden: { opacity: 0 }, } const item = { visible: { opacity: 1, x: 0 }, hidden: { opacity: 0, x: -100 }, } return ( <motion.ul initial\="hidden" whileInView\="visible" :variants\="list" \> <motion.li :variants="item" /> <motion.li :variants="item" /> <motion.li :variants="item" /> </motion.ul\> ) ### Orchestration By default, this children animations will start simultaneously with the parent. But with variants we gain access to new `transition` props like `when`, `delayChildren`, `staggerChildren` and `staggerDirection`. const list = { visible: { opacity: 1, transition: { when: "beforeChildren", staggerChildren: 0.3, // Stagger children by .3 seconds }, }, hidden: { opacity: 0, transition: { when: "afterChildren", }, }, } ### Dynamic variants Each variant can be defined as a function that resolves when a variant is made active. const variants = { hidden: { opacity: 0 }, visible: (index) \=> ({ opacity: 1, transition: { delay: index \* 0.3 } }) } These functions are provided a single argument, which is passed via the `custom` prop: <motion.div v-for\="(item,index) in items" :custom\="index" :variants\="variants" /> This way, variants can be resolved differently for each animating element. ## Animation controls Declarative animations are ideal for most UI interactions. But sometimes we need to take manual control over animation playback. The `useAnimate` hook can be used for: * Animating any HTML/SVG element (not just `motion` components). * Complex animation sequences. * Controlling animations with `time`, `speed`, `play()`, `pause()` and other playback controls. <script setup\> const \[scope, animate\] = useAnimate() watch(scope, () \=> { const controls = animate(\[ \[scope.current, { x: "100%" }\], \["li", { opacity: 1 }\] \]) controls.speed = 0.8 return () \=> controls.stop() }) </script\> <template\> <ul ref\="scope"\> <li /> <li /> <li /> </ul\> </template\> ## Animate content By passing a `MotionValue` as value prop to a `RowValue` component, it will render its latest value in the HTML. <script setup\> import { useMotionValue, motion, animate, RowValue } from "motion-v" import { onMount, onUnmount } from "vue" const count = useMotionValue(0) let controls onMount(()\=>{ controls = animate(count, 100, { duration: 5 }) }) onUnmount(()\=>{ controls.stop() }) </script\> <template\> <motion.pre\><RowValue :value\="count"/></motion.pre\> </template\> This is more performant than setting Vue state as the `RowValue` component will set `innerHTML` directly. Motion for Vue offers a number of ways to animate your UI. Scaling from extremely simple prop-based animations, to more complex orchestration. ## Basic animations You'll perform almost all animations on a `<motion />` component. This is basically a DOM element with motion superpowers. import { motion } from "motion-v" For basic animations, you can update values on the `animate` prop: <motion.div :animate\="{ opacity: 1 }" /> When any value in its animate prop changes, the component will automatically animate to the new target. ## Animatable values Motion can animate any CSS value, even those that can't be animated by browsers, like `mask-image`. It supports: * Numbers: `0`, `100` etc. * Strings containing numbers: `"0vh"`, `"10px"` etc. * Colors: Hex, RGBA, HSLA. * Complex strings containing multiple numbers and/or colors (like `box-shadow`). * `display: "none"/"block"` and `visibility: "hidden"/"visible"`. ### Value type conversion In general, values can only be animated between two of the same type (i.e `"0px"` to `"100px"`). Colors can be freely animated between hex, RGBA and HSLA types. Additionally, `x`, `y`, `width`, `height`, `top`, `left`, `right` and `bottom` can animate between different value types. <motion.div :initial\='{ x: "100%" }' :animate\='{ x: "calc(100vw - 50%)" }' /> It's also possible to animate `width` and `height` in to/out of `"auto"`. <motion.div :initial\='{ height: "0px" }' :animate\='{ height: "auto" }' /> **Note:** If additionally animating `display` in to/out of `"none"`, replace this with `visibility` `"hidden"` as elements with `display: none` can't be measured. ### Transforms Unlike CSS, Motion can animate every transform axis independently: * Translate: `x`, `y`, `z` * Scale: `scale`, `scaleX`, `scaleY` * Rotate: `rotate`, `rotateX`, `rotateY`, `rotateZ` * Skew: `skew`, `skewX`, `skewY` * Perspective: `transformPerspective` `motion` components have enhanced `style` props, allowing you to set individual transforms: <motion.section :style\="{ x: -20 }" /> Animating transforms independently provides great flexibility, especially around gestures. <motion.button :whileHover\="{ scale: 1.1 }" whilePress\="{ scale: 0.9 }" /> Independent transforms perform great, but Motion's hybrid engine also uniquely offers hardware acceleration by setting `transform` directly. <motion.li :initial\='{ transform: "translateX(-100px)" }' :animate\='{ transform: "translateX(0px)" }' :transition\='{ type: "spring" }' /> **SVG note:** For SVG components, `x` and `y` **attributes** can be set using `attrX` and `attrY`. ### Transform origin `transform-origin` has three shortcut values that can be set and animated individually: * `originX` * `originY` * `originZ` If set as numbers, `originX` and `Y` default to a progress value between `0` and `1`. `originZ` defaults to pixels. <motion.div :style\='{ originX: 0.5 }' /> ### CSS variables Motion for Vue can animate the value of CSS variables, and also use CSS variables as animation targets. #### Animating CSS variables Sometimes it's convenient to be able to animate a CSS variable to animate many children: <motion.ul :initial\="{ '--rotate': '0deg' }" :animate\="{ '--rotate': '360deg' }" :transition\="{ duration: 2, repeat: Infinity }" \> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> </motion.ul\> **Note:** Animating the value of a CSS variable **always triggers paint**, therefore it can be more performant to use `MotionValue`s to setup this kind of animation. ### CSS variables as animation targets HTML `motion` components accept animation targets with CSS variables: <motion.li :animate\="{ backgroundColor: 'var(--action-bg)' }" /> #### SVG line drawing Line drawing animations can be created with many different SVG elements using three special properties: `pathLength`, `pathSpacing` and `pathOffset`. <motion.path :initial\="{ pathLength: 0 }" :animate\="{ pathLength: 1 }" /> All three are set as a progress value between `0` and `1`, `1` representing the total length of the path. Path animations are compatible with `circle`, `ellipse`, `line`, `path`, `polygon`, `polyline` and `rect` elements. ## Transitions By default, Motion will create appropriate transitions for snappy animations based on the type of value being animated. For instance, physical properties like `x` or `scale` are animated with spring physics, whereas values like `opacity` or `color` are animated with duration-based easing curves. However, you can define your own animations via the `transition` prop. <motion.div :animate\="{ x: 100 }" :transition\="{ ease: 'easeOut', duration: 2 }" /> ## Enter animations When a `motion` component is first created, it'll automatically animate to the values in `animate` if they're different from those initially rendered, which you can either do via CSS or via the `initial` prop. <motion.li :initial\="{ opacity: 0, scale: 0 }" :animate\="{ opacity: 1, scale: 1 }" /> You can also disable the enter animation entirely by setting `:initial="false"`. This will make the element render with the values defined in `animate`. <motion.div :initial\="false" :animate\="{ y: 100 }" /> ## Exit animations You can also easily animate elements as they exit the DOM. In Vue, when a component is removed, it's usually removed instantly. Motion provides the `AnimatePresence` component which keeps elements in the DOM while they perform an `exit` animation. <AnimatePresence\> <motion.div v-if\="isVisible" key\="modal" :initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :exit="{ opacity: 0 }" /> </AnimatePresence\> ## Keyframes Values in `animate` can be set as a series of keyframes. This will animate through each value in sequence. <motion.div :animate\="{ x: \[0, 100, 0\] }" /> We can use a value's current state as the initial keyframe by setting it to `null`. <motion.div :animate\="{ x: \[null, 100, 0\] }" /> This way, if a keyframe animation is interrupting another animation, the transition will feel more natural. By default, each keyframe is spaced naturally throughout the animation. You can override this by setting the `times` option via `transition`. `times` is an array of progress values between `0` and `1`, defining where in the animation each keyframe should be positioned. <motion.circle :cx\="500" :animate\="{ cx: \[null, 100, 200\], transition={{ duration: 3, times: \[0, 0.2, 1\] }} }" /> ## Gesture animations Motion for Vue has shortcut props for animating to/from a target when a gesture starts/ends. <motion.button :initial\="{ opacity: 0 }" :whileHover\="{ backgroundColor: 'rgba(220, 220, 220, 1)' }" :whilePress\="{ backgroundColor: 'rgba(255, 255, 255, 1)' }" :whileInView\="{ opacity: 1 }" /> It supports `hover`, `press`, `drag`, `focus` and `inView`. ## Variants Setting `animate` as a target is useful for simple, single-element animations. But sometimes we want to orchestrate animations that propagate throughout the DOM. We can do so with variants. Variants are a set of named targets. const variants = { visible: { opacity: 1 }, hidden: { opacity: 0 }, } They're passed to `motion` components via the `variants` prop: <motion.div :variants\="variants" /> These variants can now be referred to by a label, wherever you can define an animation target: <motion.div :variants\="variants" initial\="hidden" whileInView\="visible" /> You can also define multiple variants via an array: :animate\="\['visible', 'danger'\]" ### Propagation This is already useful for reusing and combining animation targets. But it becomes powerful for orchestrating animations throughout trees. Variants will flow down through `motion` components. So in this example when the `ul` enters the viewport, all of its children with a "visible" variant will also animate in: const list = { visible: { opacity: 1 }, hidden: { opacity: 0 }, } const item = { visible: { opacity: 1, x: 0 }, hidden: { opacity: 0, x: -100 }, } return ( <motion.ul initial\="hidden" whileInView\="visible" :variants\="list" \> <motion.li :variants="item" /> <motion.li :variants="item" /> <motion.li :variants="item" /> </motion.ul\> ) ### Orchestration By default, this children animations will start simultaneously with the parent. But with variants we gain access to new `transition` props like `when`, `delayChildren`, `staggerChildren` and `staggerDirection`. const list = { visible: { opacity: 1, transition: { when: "beforeChildren", staggerChildren: 0.3, // Stagger children by .3 seconds }, }, hidden: { opacity: 0, transition: { when: "afterChildren", }, }, } ### Dynamic variants Each variant can be defined as a function that resolves when a variant is made active. const variants = { hidden: { opacity: 0 }, visible: (index) \=> ({ opacity: 1, transition: { delay: index \* 0.3 } }) } These functions are provided a single argument, which is passed via the `custom` prop: <motion.div v-for\="(item,index) in items" :custom\="index" :variants\="variants" /> This way, variants can be resolved differently for each animating element. ## Animation controls Declarative animations are ideal for most UI interactions. But sometimes we need to take manual control over animation playback. The `useAnimate` hook can be used for: * Animating any HTML/SVG element (not just `motion` components). * Complex animation sequences. * Controlling animations with `time`, `speed`, `play()`, `pause()` and other playback controls. <script setup\> const \[scope, animate\] = useAnimate() watch(scope, () \=> { const controls = animate(\[ \[scope.current, { x: "100%" }\], \["li", { opacity: 1 }\] \]) controls.speed = 0.8 return () \=> controls.stop() }) </script\> <template\> <ul ref\="scope"\> <li /> <li /> <li /> </ul\> </template\> ## Animate content By passing a `MotionValue` as value prop to a `RowValue` component, it will render its latest value in the HTML. <script setup\> import { useMotionValue, motion, animate, RowValue } from "motion-v" import { onMount, onUnmount } from "vue" const count = useMotionValue(0) let controls onMount(()\=>{ controls = animate(count, 100, { duration: 5 }) }) onUnmount(()\=>{ controls.stop() }) </script\> <template\> <motion.pre\><RowValue :value\="count"/></motion.pre\> </template\> This is more performant than setting Vue state as the `RowValue` component will set `innerHTML` directly. Motion for Vue offers a number of ways to animate your UI. Scaling from extremely simple prop-based animations, to more complex orchestration. ## Basic animations You'll perform almost all animations on a `<motion />` component. This is basically a DOM element with motion superpowers. import { motion } from "motion-v" For basic animations, you can update values on the `animate` prop: <motion.div :animate\="{ opacity: 1 }" /> When any value in its animate prop changes, the component will automatically animate to the new target. ## Animatable values Motion can animate any CSS value, even those that can't be animated by browsers, like `mask-image`. It supports: * Numbers: `0`, `100` etc. * Strings containing numbers: `"0vh"`, `"10px"` etc. * Colors: Hex, RGBA, HSLA. * Complex strings containing multiple numbers and/or colors (like `box-shadow`). * `display: "none"/"block"` and `visibility: "hidden"/"visible"`. ### Value type conversion In general, values can only be animated between two of the same type (i.e `"0px"` to `"100px"`). Colors can be freely animated between hex, RGBA and HSLA types. Additionally, `x`, `y`, `width`, `height`, `top`, `left`, `right` and `bottom` can animate between different value types. <motion.div :initial\='{ x: "100%" }' :animate\='{ x: "calc(100vw - 50%)" }' /> It's also possible to animate `width` and `height` in to/out of `"auto"`. <motion.div :initial\='{ height: "0px" }' :animate\='{ height: "auto" }' /> **Note:** If additionally animating `display` in to/out of `"none"`, replace this with `visibility` `"hidden"` as elements with `display: none` can't be measured. ### Transforms Unlike CSS, Motion can animate every transform axis independently: * Translate: `x`, `y`, `z` * Scale: `scale`, `scaleX`, `scaleY` * Rotate: `rotate`, `rotateX`, `rotateY`, `rotateZ` * Skew: `skew`, `skewX`, `skewY` * Perspective: `transformPerspective` `motion` components have enhanced `style` props, allowing you to set individual transforms: <motion.section :style\="{ x: -20 }" /> Animating transforms independently provides great flexibility, especially around gestures. <motion.button :whileHover\="{ scale: 1.1 }" whilePress\="{ scale: 0.9 }" /> Independent transforms perform great, but Motion's hybrid engine also uniquely offers hardware acceleration by setting `transform` directly. <motion.li :initial\='{ transform: "translateX(-100px)" }' :animate\='{ transform: "translateX(0px)" }' :transition\='{ type: "spring" }' /> **SVG note:** For SVG components, `x` and `y` **attributes** can be set using `attrX` and `attrY`. ### Transform origin `transform-origin` has three shortcut values that can be set and animated individually: * `originX` * `originY` * `originZ` If set as numbers, `originX` and `Y` default to a progress value between `0` and `1`. `originZ` defaults to pixels. <motion.div :style\='{ originX: 0.5 }' /> ### CSS variables Motion for Vue can animate the value of CSS variables, and also use CSS variables as animation targets. #### Animating CSS variables Sometimes it's convenient to be able to animate a CSS variable to animate many children: <motion.ul :initial\="{ '--rotate': '0deg' }" :animate\="{ '--rotate': '360deg' }" :transition\="{ duration: 2, repeat: Infinity }" \> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> <li :style\="{ transform: 'rotate(var(--rotate))' }" /> </motion.ul\> **Note:** Animating the value of a CSS variable **always triggers paint**, therefore it can be more performant to use `MotionValue`s to setup this kind of animation. ### CSS variables as animation targets HTML `motion` components accept animation targets with CSS variables: <motion.li :animate\="{ backgroundColor: 'var(--action-bg)' }" /> #### SVG line drawing Line drawing animations can be created with many different SVG elements using three special properties: `pathLength`, `pathSpacing` and `pathOffset`. <motion.path :initial\="{ pathLength: 0 }" :animate\="{ pathLength: 1 }" /> All three are set as a progress value between `0` and `1`, `1` representing the total length of the path. Path animations are compatible with `circle`, `ellipse`, `line`, `path`, `polygon`, `polyline` and `rect` elements. ## Transitions By default, Motion will create appropriate transitions for snappy animations based on the type of value being animated. For instance, physical properties like `x` or `scale` are animated with spring physics, whereas values like `opacity` or `color` are animated with duration-based easing curves. However, you can define your own animations via the `transition` prop. <motion.div :animate\="{ x: 100 }" :transition\="{ ease: 'easeOut', duration: 2 }" /> ## Enter animations When a `motion` component is first created, it'll automatically animate to the values in `animate` if they're different from those initially rendered, which you can either do via CSS or via the `initial` prop. <motion.li :initial\="{ opacity: 0, scale: 0 }" :animate\="{ opacity: 1, scale: 1 }" /> You can also disable the enter animation entirely by setting `:initial="false"`. This will make the element render with the values defined in `animate`. <motion.div :initial\="false" :animate\="{ y: 100 }" /> ## Exit animations You can also easily animate elements as they exit the DOM. In Vue, when a component is removed, it's usually removed instantly. Motion provides the `AnimatePresence` component which keeps elements in the DOM while they perform an `exit` animation. <AnimatePresence\> <motion.div v-if\="isVisible" key\="modal" :initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :exit="{ opacity: 0 }" /> </AnimatePresence\> ## Keyframes Values in `animate` can be set as a series of keyframes. This will animate through each value in sequence. <motion.div :animate\="{ x: \[0, 100, 0\] }" /> We can use a value's current state as the initial keyframe by setting it to `null`. <motion.div :animate\="{ x: \[null, 100, 0\] }" /> This way, if a keyframe animation is interrupting another animation, the transition will feel more natural. By default, each keyframe is spaced naturally throughout the animation. You can override this by setting the `times` option via `transition`. `times` is an array of progress values between `0` and `1`, defining where in the animation each keyframe should be positioned. <motion.circle :cx\="500" :animate\="{ cx: \[null, 100, 200\], transition={{ duration: 3, times: \[0, 0.2, 1\] }} }" /> ## Gesture animations Motion for Vue has shortcut props for animating to/from a target when a gesture starts/ends. <motion.button :initial\="{ opacity: 0 }" :whileHover\="{ backgroundColor: 'rgba(220, 220, 220, 1)' }" :whilePress\="{ backgroundColor: 'rgba(255, 255, 255, 1)' }" :whileInView\="{ opacity: 1 }" /> It supports `hover`, `press`, `drag`, `focus` and `inView`. ## Variants Setting `animate` as a target is useful for simple, single-element animations. But sometimes we want to orchestrate animations that propagate throughout the DOM. We can do so with variants. Variants are a set of named targets. const variants = { visible: { opacity: 1 }, hidden: { opacity: 0 }, } They're passed to `motion` components via the `variants` prop: <motion.div :variants\="variants" /> These variants can now be referred to by a label, wherever you can define an animation target: <motion.div :variants\="variants" initial\="hidden" whileInView\="visible" /> You can also define multiple variants via an array: :animate\="\['visible', 'danger'\]" ### Propagation This is already useful for reusing and combining animation targets. But it becomes powerful for orchestrating animations throughout trees. Variants will flow down through `motion` components. So in this example when the `ul` enters the viewport, all of its children with a "visible" variant will also animate in: const list = { visible: { opacity: 1 }, hidden: { opacity: 0 }, } const item = { visible: { opacity: 1, x: 0 }, hidden: { opacity: 0, x: -100 }, } return ( <motion.ul initial\="hidden" whileInView\="visible" :variants\="list" \> <motion.li :variants="item" /> <motion.li :variants="item" /> <motion.li :variants="item" /> </motion.ul\> ) ### Orchestration By default, this children animations will start simultaneously with the parent. But with variants we gain access to new `transition` props like `when`, `delayChildren`, `staggerChildren` and `staggerDirection`. const list = { visible: { opacity: 1, transition: { when: "beforeChildren", staggerChildren: 0.3, // Stagger children by .3 seconds }, }, hidden: { opacity: 0, transition: { when: "afterChildren", }, }, } ### Dynamic variants Each variant can be defined as a function that resolves when a variant is made active. const variants = { hidden: { opacity: 0 }, visible: (index) \=> ({ opacity: 1, transition: { delay: index \* 0.3 } }) } These functions are provided a single argument, which is passed via the `custom` prop: <motion.div v-for\="(item,index) in items" :custom\="index" :variants\="variants" /> This way, variants can be resolved differently for each animating element. ## Animation controls Declarative animations are ideal for most UI interactions. But sometimes we need to take manual control over animation playback. The `useAnimate` hook can be used for: * Animating any HTML/SVG element (not just `motion` components). * Complex animation sequences. * Controlling animations with `time`, `speed`, `play()`, `pause()` and other playback controls. <script setup\> const \[scope, animate\] = useAnimate() watch(scope, () \=> { const controls = animate(\[ \[scope.current, { x: "100%" }\], \["li", { opacity: 1 }\] \]) controls.speed = 0.8 return () \=> controls.stop() }) </script\> <template\> <ul ref\="scope"\> <li /> <li /> <li /> </ul\> </template\> ## Animate content By passing a `MotionValue` as value prop to a `RowValue` component, it will render its latest value in the HTML. <script setup\> import { useMotionValue, motion, animate, RowValue } from "motion-v" import { onMount, onUnmount } from "vue" const count = useMotionValue(0) let controls onMount(()\=>{ controls = animate(count, 100, { duration: 5 }) }) onUnmount(()\=>{ controls.stop() }) </script\> <template\> <motion.pre\><RowValue :value\="count"/></motion.pre\> </template\> This is more performant than setting Vue state as the `RowValue` component will set `innerHTML` directly. --- ## Page: https://motion.dev/docs/vue-gestures # Gestures Motion extends Vue's basic set of event listeners with a simple yet powerful set of UI gestures. The `motion` component currently has support for **hover**, **press**, **pan**, **drag** and **inView**. Each gesture has both a set of event listeners and a `while-` animation prop. ## Animation props `motion` components provide multiple gesture animation props: `whileHover`, `whilePress`, `whileFocus`, `whileDrag` and `whileInView`. These can define animation targets to temporarily animate to while a gesture is active. <motion.button :whileHover\="{ scale: 1.2, transition: { duration: 1 }, }" :whilePress\="{ scale: 0.9 }" /> All props can be set either as a target of values to animate to, or the name of any variants defined via the `variants` prop. Variants will flow down through children as normal. <motion.button whilePress\="press" whileHover\="hover" :variants\="buttonVariants" \> <svg\> <motion.path :variants\="iconVariants" /> </svg\> </motion.button\> ## Gestures ### Hover The hover gesture detects when a pointer hovers over or leaves a component. It differs from `onMouseEnter` and `onMouseLeave` in that hover is guaranteed to only fire as a result of actual mouse events (as opposed to browser-generated mice events emulated from touch input). <motion.a :whileHover\="{ scale: 1.2 }" @hoverStart\="event => {}" @hoverEnd\="event => {}" ### Press The press gesture detects when the **primary pointer** (like a left click or first touch point) presses down and releases on the same component. <motion.button :whilePress\="{ scale: 0.9, rotate: 3 }" /> It will fire a `press` event when the tap or click ends on the same component it started on, and a `pressCancel` event if the press or click ends outside the component. If the pressable component is a child of a draggable component, it'll automatically cancel the press gesture if the pointer moves further than 3 pixels during the gesture. #### Accessibility Elements with press events are keyboard-accessible. Any element with a press prop will be able to receive focus and `Enter` can be used to trigger press events on focused elements. * Pressing `Enter` down will trigger `onPressStart` and `whilePress` * Releasing `Enter` will trigger `onPress` * If the element loses focus before `Enter` is released, `onPressCancel` will fire. ### Pan The pan gesture recognises when a pointer presses down on a component and moves further than 3 pixels. The pan gesture is ended when the pointer is released. <motion.div @pan\="(e, pointInfo) => {}" /> Pan doesn't currently have an associated `while-` prop. **Note:** For pan gestures to work correctly with touch input, the element needs touch scrolling to be disabled on either x/y or both axis with the `touch-action` CSS rule. ### Drag The drag gesture applies pointer movement to the x and/or y axis of the component. <motion.div drag :whileDrag\="{ scale: 1.2, backgroundColor: '#f00' }" /> By default, when the drag ends the element will perform an inertia animation with the ending velocity. This can be disabled by setting `dragMomentum` to `false`, or changed via the `dragTransition` prop. #### Constraints It's also possible to set `dragConstraints`, either as an object with `top`, `left`, `right`, and `bottom` values, measured in pixels. <motion.div drag\="x" :dragConstraints\="{ left: 0, right: 300 }" /> Or, it can accept an HTMLElement `ref` value. You can get the component's DOM ref value using `useDomRef` from `motion-v`, and pass it both to the draggable component's `dragConstraints` prop and the ref of the component you want to use as constraints. <script setup\> import { useDomRef } from "motion-v" const constraintsRef = useDomRef() </script\> <template\> <motion.div ref\="constraintsRef"\> <motion.div drag :dragConstraints\="constraintsRef" /> </motion.div\> </template\> By default, dragging the element outside the constraints will tug with some elasticity. This can be changed by setting `dragElastic` to a value between `0` and `1`, where `0` equals no motion and `1` equals full motion outside the constraints. #### Direction locking It's possible to lock an element to the first axis it's dragged on by setting `dragDirectionLock`. <motion.div drag dragDirectionLock @directionLock\="callback" /> Each time the drag gesture starts, the direction of pointer travel will be detected and the element will be draggable only on this axis. ### Focus The focus gesture detects when a component gains or loses focus by the same rules as the CSS :focus-visible selector. Typically, this is when an `input` receives focus by any means, and when other elements receive focus by accessible means (like via keyboard navigation). <motion.a :whileFocus\="{ scale: 1.2 }" href\="#" /> ## Event propagation Children can stop pointer events propagating to parent `motion` components using the `Capture` Vue props. For instance, a child can stop drag and tap gestures and their related `while` animations from firing on parents by passing `e.stopPropagation()` to `onPointerDownCapture`. <motion.div :whilePress\="{ scale: 2 }"\> <button @pointerDownCapture\="e \=> e.stopPropagation()" /> </motion.div\> ## Note: SVG filters Gestures aren't recognised on SVG `filter` components, as these elements don't have a physical presence and therefore don't receive events. You can instead add `while-` props and event handlers to a parent and use variants to animate these elements. <template\> <motion.svg whileHover\="hover"\> <filter id\="blur"\> <motion.feGaussianBlur :stdDeviation="0" :variants="{ hover: { stdDeviation: 2 } }" /> </filter\> </motion.svg\> </template\> Motion extends Vue's basic set of event listeners with a simple yet powerful set of UI gestures. The `motion` component currently has support for **hover**, **press**, **pan**, **drag** and **inView**. Each gesture has both a set of event listeners and a `while-` animation prop. ## Animation props `motion` components provide multiple gesture animation props: `whileHover`, `whilePress`, `whileFocus`, `whileDrag` and `whileInView`. These can define animation targets to temporarily animate to while a gesture is active. <motion.button :whileHover\="{ scale: 1.2, transition: { duration: 1 }, }" :whilePress\="{ scale: 0.9 }" /> All props can be set either as a target of values to animate to, or the name of any variants defined via the `variants` prop. Variants will flow down through children as normal. <motion.button whilePress\="press" whileHover\="hover" :variants\="buttonVariants" \> <svg\> <motion.path :variants\="iconVariants" /> </svg\> </motion.button\> ## Gestures ### Hover The hover gesture detects when a pointer hovers over or leaves a component. It differs from `onMouseEnter` and `onMouseLeave` in that hover is guaranteed to only fire as a result of actual mouse events (as opposed to browser-generated mice events emulated from touch input). <motion.a :whileHover\="{ scale: 1.2 }" @hoverStart\="event => {}" @hoverEnd\="event => {}" ### Press The press gesture detects when the **primary pointer** (like a left click or first touch point) presses down and releases on the same component. <motion.button :whilePress\="{ scale: 0.9, rotate: 3 }" /> It will fire a `press` event when the tap or click ends on the same component it started on, and a `pressCancel` event if the press or click ends outside the component. If the pressable component is a child of a draggable component, it'll automatically cancel the press gesture if the pointer moves further than 3 pixels during the gesture. #### Accessibility Elements with press events are keyboard-accessible. Any element with a press prop will be able to receive focus and `Enter` can be used to trigger press events on focused elements. * Pressing `Enter` down will trigger `onPressStart` and `whilePress` * Releasing `Enter` will trigger `onPress` * If the element loses focus before `Enter` is released, `onPressCancel` will fire. ### Pan The pan gesture recognises when a pointer presses down on a component and moves further than 3 pixels. The pan gesture is ended when the pointer is released. <motion.div @pan\="(e, pointInfo) => {}" /> Pan doesn't currently have an associated `while-` prop. **Note:** For pan gestures to work correctly with touch input, the element needs touch scrolling to be disabled on either x/y or both axis with the `touch-action` CSS rule. ### Drag The drag gesture applies pointer movement to the x and/or y axis of the component. <motion.div drag :whileDrag\="{ scale: 1.2, backgroundColor: '#f00' }" /> By default, when the drag ends the element will perform an inertia animation with the ending velocity. This can be disabled by setting `dragMomentum` to `false`, or changed via the `dragTransition` prop. #### Constraints It's also possible to set `dragConstraints`, either as an object with `top`, `left`, `right`, and `bottom` values, measured in pixels. <motion.div drag\="x" :dragConstraints\="{ left: 0, right: 300 }" /> Or, it can accept an HTMLElement `ref` value. You can get the component's DOM ref value using `useDomRef` from `motion-v`, and pass it both to the draggable component's `dragConstraints` prop and the ref of the component you want to use as constraints. <script setup\> import { useDomRef } from "motion-v" const constraintsRef = useDomRef() </script\> <template\> <motion.div ref\="constraintsRef"\> <motion.div drag :dragConstraints\="constraintsRef" /> </motion.div\> </template\> By default, dragging the element outside the constraints will tug with some elasticity. This can be changed by setting `dragElastic` to a value between `0` and `1`, where `0` equals no motion and `1` equals full motion outside the constraints. #### Direction locking It's possible to lock an element to the first axis it's dragged on by setting `dragDirectionLock`. <motion.div drag dragDirectionLock @directionLock\="callback" /> Each time the drag gesture starts, the direction of pointer travel will be detected and the element will be draggable only on this axis. ### Focus The focus gesture detects when a component gains or loses focus by the same rules as the CSS :focus-visible selector. Typically, this is when an `input` receives focus by any means, and when other elements receive focus by accessible means (like via keyboard navigation). <motion.a :whileFocus\="{ scale: 1.2 }" href\="#" /> ## Event propagation Children can stop pointer events propagating to parent `motion` components using the `Capture` Vue props. For instance, a child can stop drag and tap gestures and their related `while` animations from firing on parents by passing `e.stopPropagation()` to `onPointerDownCapture`. <motion.div :whilePress\="{ scale: 2 }"\> <button @pointerDownCapture\="e \=> e.stopPropagation()" /> </motion.div\> ## Note: SVG filters Gestures aren't recognised on SVG `filter` components, as these elements don't have a physical presence and therefore don't receive events. You can instead add `while-` props and event handlers to a parent and use variants to animate these elements. <template\> <motion.svg whileHover\="hover"\> <filter id\="blur"\> <motion.feGaussianBlur :stdDeviation="0" :variants="{ hover: { stdDeviation: 2 } }" /> </filter\> </motion.svg\> </template\> Motion extends Vue's basic set of event listeners with a simple yet powerful set of UI gestures. The `motion` component currently has support for **hover**, **press**, **pan**, **drag** and **inView**. Each gesture has both a set of event listeners and a `while-` animation prop. ## Animation props `motion` components provide multiple gesture animation props: `whileHover`, `whilePress`, `whileFocus`, `whileDrag` and `whileInView`. These can define animation targets to temporarily animate to while a gesture is active. <motion.button :whileHover\="{ scale: 1.2, transition: { duration: 1 }, }" :whilePress\="{ scale: 0.9 }" /> All props can be set either as a target of values to animate to, or the name of any variants defined via the `variants` prop. Variants will flow down through children as normal. <motion.button whilePress\="press" whileHover\="hover" :variants\="buttonVariants" \> <svg\> <motion.path :variants\="iconVariants" /> </svg\> </motion.button\> ## Gestures ### Hover The hover gesture detects when a pointer hovers over or leaves a component. It differs from `onMouseEnter` and `onMouseLeave` in that hover is guaranteed to only fire as a result of actual mouse events (as opposed to browser-generated mice events emulated from touch input). <motion.a :whileHover\="{ scale: 1.2 }" @hoverStart\="event => {}" @hoverEnd\="event => {}" ### Press The press gesture detects when the **primary pointer** (like a left click or first touch point) presses down and releases on the same component. <motion.button :whilePress\="{ scale: 0.9, rotate: 3 }" /> It will fire a `press` event when the tap or click ends on the same component it started on, and a `pressCancel` event if the press or click ends outside the component. If the pressable component is a child of a draggable component, it'll automatically cancel the press gesture if the pointer moves further than 3 pixels during the gesture. #### Accessibility Elements with press events are keyboard-accessible. Any element with a press prop will be able to receive focus and `Enter` can be used to trigger press events on focused elements. * Pressing `Enter` down will trigger `onPressStart` and `whilePress` * Releasing `Enter` will trigger `onPress` * If the element loses focus before `Enter` is released, `onPressCancel` will fire. ### Pan The pan gesture recognises when a pointer presses down on a component and moves further than 3 pixels. The pan gesture is ended when the pointer is released. <motion.div @pan\="(e, pointInfo) => {}" /> Pan doesn't currently have an associated `while-` prop. **Note:** For pan gestures to work correctly with touch input, the element needs touch scrolling to be disabled on either x/y or both axis with the `touch-action` CSS rule. ### Drag The drag gesture applies pointer movement to the x and/or y axis of the component. <motion.div drag :whileDrag\="{ scale: 1.2, backgroundColor: '#f00' }" /> By default, when the drag ends the element will perform an inertia animation with the ending velocity. This can be disabled by setting `dragMomentum` to `false`, or changed via the `dragTransition` prop. #### Constraints It's also possible to set `dragConstraints`, either as an object with `top`, `left`, `right`, and `bottom` values, measured in pixels. <motion.div drag\="x" :dragConstraints\="{ left: 0, right: 300 }" /> Or, it can accept an HTMLElement `ref` value. You can get the component's DOM ref value using `useDomRef` from `motion-v`, and pass it both to the draggable component's `dragConstraints` prop and the ref of the component you want to use as constraints. <script setup\> import { useDomRef } from "motion-v" const constraintsRef = useDomRef() </script\> <template\> <motion.div ref\="constraintsRef"\> <motion.div drag :dragConstraints\="constraintsRef" /> </motion.div\> </template\> By default, dragging the element outside the constraints will tug with some elasticity. This can be changed by setting `dragElastic` to a value between `0` and `1`, where `0` equals no motion and `1` equals full motion outside the constraints. #### Direction locking It's possible to lock an element to the first axis it's dragged on by setting `dragDirectionLock`. <motion.div drag dragDirectionLock @directionLock\="callback" /> Each time the drag gesture starts, the direction of pointer travel will be detected and the element will be draggable only on this axis. ### Focus The focus gesture detects when a component gains or loses focus by the same rules as the CSS :focus-visible selector. Typically, this is when an `input` receives focus by any means, and when other elements receive focus by accessible means (like via keyboard navigation). <motion.a :whileFocus\="{ scale: 1.2 }" href\="#" /> ## Event propagation Children can stop pointer events propagating to parent `motion` components using the `Capture` Vue props. For instance, a child can stop drag and tap gestures and their related `while` animations from firing on parents by passing `e.stopPropagation()` to `onPointerDownCapture`. <motion.div :whilePress\="{ scale: 2 }"\> <button @pointerDownCapture\="e \=> e.stopPropagation()" /> </motion.div\> ## Note: SVG filters Gestures aren't recognised on SVG `filter` components, as these elements don't have a physical presence and therefore don't receive events. You can instead add `while-` props and event handlers to a parent and use variants to animate these elements. <template\> <motion.svg whileHover\="hover"\> <filter id\="blur"\> <motion.feGaussianBlur :stdDeviation="0" :variants="{ hover: { stdDeviation: 2 } }" /> </filter\> </motion.svg\> </template\> --- ## Page: https://motion.dev/docs/vue-scroll-animations # Scroll animations There are two types of scroll animations: * **Scroll-triggered:** A normal animation is triggered when an element enters the viewport. * **Scroll-linked:** Values are linked directly to scroll progress. Motion is capable of both types of animation. ## Scroll-triggered animations Scroll-triggered animations are just normal animations that fire when an element enters or leaves the viewport. Motion offers the `whileInView` prop to set an animation target or variant when the element enters the view. <motion.div :initial\="{ opacity: 0 }" :whileInView\="{ opacity: 1 }" /> ### One-time animations With the `inViewOptions` , it's possible to set `once: true` so once an element has entered the viewport, it won't animate back out. <motion.div initial\="hidden" whileInView\="visible" :inViewOptions\="{ once: true }" /> ### Changing scroll container By default, the element will be considered within the viewport when it enters/leaves the **window**. This can be changed by providing the `ref` of another scrollable element. <script setup\> import { ref } from "vue" const scrollRef = ref(null) </script\> <template\> <div ref\="scrollRef" :style\="{ overflow: 'scroll' }"\> <motion.div :initial\="{ opacity: 0 }" :whileInView\="{ opacity: 1 }" :inViewOptions\="{ root: scrollRef }" /> </div\> </template\> For more configuration options, checkout the `motion` component API reference. ### Setting state It's also possible to set state when any element (not just a `motion` component) enters and leaves the viewport with the `useInView` hook. ## Scroll-linked animations Scroll-linked animations are created using motion values and the `useScroll` hook. `useScroll` returns four motion values, two that store scroll offset in pixels (`scrollX` and `scrollY`) and two that store scroll progress as a value between `0` and `1`. These motion values can be passed directly to specific styles. For instance, passing `scrollYProgress` to `scaleX` works great as a progress bar. <script\> import { useScroll } from "motion-v" const { scrollYProgress } = useScroll(); </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> ### Value smoothing This value can be smoothed by passing it through `useSpring`. <script setup\> const { scrollYProgress } = useScroll(); const scaleX = useSpring(scrollYProgress, { stiffness: 100, damping: 30, restDelta: 0.001 }) </script\> <template\> <motion.div :style\="{ scaleX }" /> </template\> ### Transform other values With the `useTransform` hook, it's easy to use the progress motion values to mix between any value, like colors: <script setup\> const backgroundColor = useTransform( scrollYProgress, \[0, 0.5, 1\], \["#f00", "#0f0", "#00f"\] ) </script\> <template\> <motion.div :style\="{ backgroundColor }" /> </template\> ### Examples #### Track element scroll offset #### Track element within viewport #### Parallax #### Scroll velocity and direction Read the full `useScroll` docs to discover more about creating the above effects. There are two types of scroll animations: * **Scroll-triggered:** A normal animation is triggered when an element enters the viewport. * **Scroll-linked:** Values are linked directly to scroll progress. Motion is capable of both types of animation. ## Scroll-triggered animations Scroll-triggered animations are just normal animations that fire when an element enters or leaves the viewport. Motion offers the `whileInView` prop to set an animation target or variant when the element enters the view. <motion.div :initial\="{ opacity: 0 }" :whileInView\="{ opacity: 1 }" /> ### One-time animations With the `inViewOptions` , it's possible to set `once: true` so once an element has entered the viewport, it won't animate back out. <motion.div initial\="hidden" whileInView\="visible" :inViewOptions\="{ once: true }" /> ### Changing scroll container By default, the element will be considered within the viewport when it enters/leaves the **window**. This can be changed by providing the `ref` of another scrollable element. <script setup\> import { ref } from "vue" const scrollRef = ref(null) </script\> <template\> <div ref\="scrollRef" :style\="{ overflow: 'scroll' }"\> <motion.div :initial\="{ opacity: 0 }" :whileInView\="{ opacity: 1 }" :inViewOptions\="{ root: scrollRef }" /> </div\> </template\> For more configuration options, checkout the `motion` component API reference. ### Setting state It's also possible to set state when any element (not just a `motion` component) enters and leaves the viewport with the `useInView` hook. ## Scroll-linked animations Scroll-linked animations are created using motion values and the `useScroll` hook. `useScroll` returns four motion values, two that store scroll offset in pixels (`scrollX` and `scrollY`) and two that store scroll progress as a value between `0` and `1`. These motion values can be passed directly to specific styles. For instance, passing `scrollYProgress` to `scaleX` works great as a progress bar. <script\> import { useScroll } from "motion-v" const { scrollYProgress } = useScroll(); </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> ### Value smoothing This value can be smoothed by passing it through `useSpring`. <script setup\> const { scrollYProgress } = useScroll(); const scaleX = useSpring(scrollYProgress, { stiffness: 100, damping: 30, restDelta: 0.001 }) </script\> <template\> <motion.div :style\="{ scaleX }" /> </template\> ### Transform other values With the `useTransform` hook, it's easy to use the progress motion values to mix between any value, like colors: <script setup\> const backgroundColor = useTransform( scrollYProgress, \[0, 0.5, 1\], \["#f00", "#0f0", "#00f"\] ) </script\> <template\> <motion.div :style\="{ backgroundColor }" /> </template\> ### Examples #### Track element scroll offset #### Track element within viewport #### Parallax #### Scroll velocity and direction Read the full `useScroll` docs to discover more about creating the above effects. There are two types of scroll animations: * **Scroll-triggered:** A normal animation is triggered when an element enters the viewport. * **Scroll-linked:** Values are linked directly to scroll progress. Motion is capable of both types of animation. ## Scroll-triggered animations Scroll-triggered animations are just normal animations that fire when an element enters or leaves the viewport. Motion offers the `whileInView` prop to set an animation target or variant when the element enters the view. <motion.div :initial\="{ opacity: 0 }" :whileInView\="{ opacity: 1 }" /> ### One-time animations With the `inViewOptions` , it's possible to set `once: true` so once an element has entered the viewport, it won't animate back out. <motion.div initial\="hidden" whileInView\="visible" :inViewOptions\="{ once: true }" /> ### Changing scroll container By default, the element will be considered within the viewport when it enters/leaves the **window**. This can be changed by providing the `ref` of another scrollable element. <script setup\> import { ref } from "vue" const scrollRef = ref(null) </script\> <template\> <div ref\="scrollRef" :style\="{ overflow: 'scroll' }"\> <motion.div :initial\="{ opacity: 0 }" :whileInView\="{ opacity: 1 }" :inViewOptions\="{ root: scrollRef }" /> </div\> </template\> For more configuration options, checkout the `motion` component API reference. ### Setting state It's also possible to set state when any element (not just a `motion` component) enters and leaves the viewport with the `useInView` hook. ## Scroll-linked animations Scroll-linked animations are created using motion values and the `useScroll` hook. `useScroll` returns four motion values, two that store scroll offset in pixels (`scrollX` and `scrollY`) and two that store scroll progress as a value between `0` and `1`. These motion values can be passed directly to specific styles. For instance, passing `scrollYProgress` to `scaleX` works great as a progress bar. <script\> import { useScroll } from "motion-v" const { scrollYProgress } = useScroll(); </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> ### Value smoothing This value can be smoothed by passing it through `useSpring`. <script setup\> const { scrollYProgress } = useScroll(); const scaleX = useSpring(scrollYProgress, { stiffness: 100, damping: 30, restDelta: 0.001 }) </script\> <template\> <motion.div :style\="{ scaleX }" /> </template\> ### Transform other values With the `useTransform` hook, it's easy to use the progress motion values to mix between any value, like colors: <script setup\> const backgroundColor = useTransform( scrollYProgress, \[0, 0.5, 1\], \["#f00", "#0f0", "#00f"\] ) </script\> <template\> <motion.div :style\="{ backgroundColor }" /> </template\> ### Examples #### Track element scroll offset #### Track element within viewport #### Parallax #### Scroll velocity and direction Read the full `useScroll` docs to discover more about creating the above effects. --- ## Page: https://motion.dev/docs/vue-layout-animations # Layout animations Motion's industry-leading layout animations makes it easy to animate between any two layouts, even between different elements. It's as simple as a `layout` prop. <motion.div layout /> This little prop can animate previously unanimatable CSS values, like switching `justify-content` between `flex-start` and `flex-end`. <motion.div layout :style\="{ justifyContent: isOn ? 'flex-start' : 'flex-end' }" /> Or by using the `layoutId` prop, it's possible to match two elements and animate between them for some truly advanced animations. <motion.li layoutId\="item" /> It can handle anything from microinteractions to full page transitions. ## Usage Any layout change that happens as a result of a Vue re-render can be animated. <motion.div layout :style\="{ width: isOpen ? '80vw' : 0 }" /> Note that CSS changes should happen immediately via `style`, not `animate`, as `layout` will take care of the animation. Layout changes can be anything, changing `width`/`height`, number of grid columns, reordering a list, or adding/removing new items: ### Shared layout animations When a new component is added that has a `layoutId` prop that matches an existing component, it will automatically animate out from the old component. <motion.div v-if\="isSelected" layoutId\="underline" /> If the old component is still mounted when the new component enters, they will automatically crossfade from the old to the new. When removing an element to animate back to its origin layout, `AnimatePresence` can be used to keep it in the DOM until its exit animation has finished. <AnimatePresence\> <motion.div v-if\="isOpen" layoutId\="modal" /> </AnimatePresence\> ### Setting a transition Layout animations can be customised using the `transition` prop. <motion.div layout :transition\="{ duration: 0.3 }" /> If you want to set a transition specifically for **only** the layout animation, you can specify a specific `layout` transition. <motion.div layout :animate\="{ opacity: 0.5 }" :transition\="{ default: { ease: 'linear' }, layout: { duration: 0.3 } }" /> When performing a shared layout animation, the transition defined for element we're animating **to** will be used. <motion.button layoutId\="modal" @click\="() \=> isOn\=true" // This transition will be used when the modal closes :transition="{ type: 'spring' }" > Open </motion.button\> <AnimatePresence\> <motion.dialog v-if\="isOn" layoutId\="modal" // This transition will be used when the modal opens :transition="{ duration: 0.3 }" /> </AnimatePresence\> ### Animating within scrollable element To correctly animate layout within scrollable elements, we need to provide them the `layoutScroll` prop. <motion.div layoutScroll :style\="{ overflow: 'scroll' }" /> This lets Motion account for the element's scroll offset when measuring children. ### Animating within fixed containers To correctly animate layout within fixed elements, we need to provide them the `layoutRoot` prop. <motion.div layoutRoot :style\="{ position: 'fixed' }" /> This lets Motion account for the page's scroll offset when measuring children. ### Group layout animations Layout animations are triggered when a component re-renders and its layout has changed. <script setup\> const open = ref(false) </script\> <template\> <motion.div layout :style\="{ height: isOpen ? "100px" : "500px" }" @click="() => open=!open" /> </template\> But what happens when we have two or more components that don't re-render at the same time, but **do** affect each other's layout? <template\> <Accordion /> <Accordion /> </template\> When one re-renders, for performance reasons the other won't be able to detect changes to its layout. We can synchronise layout changes across multiple components by wrapping them in the `LayoutGroup component`. <script setup\> import { LayoutGroup } from "motion-v" </script\> <template\> <LayoutGroup\> <Accordion /> <Accordion /> </LayoutGroup\> </template\> When layout changes are detected in any grouped `motion` component, layout animations will trigger across all of them. ### Scale correction All layout animations are performed using the `transform` style, resulting in smooth framerates. Animating layout using transforms can sometimes visually distort children. To correct this distortion, the first child elements of the element can also be given `layout` property. Open this sandbox and try removing `layout` from the pink dot to see the difference this will make: Transforms can also distort `boxShadow` and `borderRadius`. The `motion` component will automatically correct this distortion on both props, as long as they're set as motion values. If you're not animating these values, the easiest way to do this is to set them via `style`. <motion.div layout :style\="{ borderRadius: 20 }" /> ## Troubleshooting ### The component isn't animating Ensure the component is **not** set to `display: inline`, as browsers don't apply `transform` to these elements. Ensure the component is re-rendering when you expect the layout animation to start. ### SVG layout animations are broken SVG components aren't currently supported with layout animations. SVGs don't have layout systems so it's recommended to directly animate their attributes like `cx` etc. ### The content stretches undesirably This is a natural side-effect of animating `width` and `height` with `scale`. Often, this can be fixed by providing these elements a `layout` animation and they'll be scale-corrected. <motion.section layout\> <motion.img layout /> </motion.section\> Some elements, like images or text that are changing between different aspect ratios, might be better animated with `layout="position"`. ### Border radius or box shadows are behaving strangely Animating `scale` is performant but can distort some styles like `border-radius` and `box-shadow`. Motion automatically corrects for scale distortion on these properties, but they must be set on the element via `style`. <motion.div layout :style\="{ borderRadius: '20px' }" /> ### Border looks stretched during animation Elements with a `border` may look stretched during the animation. This is for two reasons: 1. Because changing `border` triggers layout recalculations, it defeats the performance benefits of animating via `transform`. You might as well animate `width` and `height` classically. 2. `border` can't render smaller than `1px`, which limits the degree of scale correction that Motion can perform on this style. A work around is to replace `border` with a parent element with padding that acts as a `border`. <motion.div layout :style\="{ borderRadius: '10px', padding: '5px' }"\> <motion.div layout "style="{ borderRadius: '5px' }" /> </motion.div\> ## Technical reading Interested in the technical details behind layout animations? Nanda does an incredible job of explaining the challenges of animating layout with transforms using interactive examples. Matt, creator of Motion, did a talk at Vercel conference about the implementation details that is largely up to date. ## Differences with the View Transitions API More browsers are starting to support the View Transitions API, which is similar to Motion's layout animations. ### Benefits of View Transitions API The main two benefits of View Transitions is that **it's included in browsers** and **features a unique rendering system**. #### Filesize Because the View Transitions API is already included in browsers, it's cheap to implement very simple crossfade animations. However, the CSS complexity can scale quite quickly. Motion's layout animations are around 12kb but from there it's very cheap to change transitions, add springs, mark matching #### Rendering Whereas Motion animates the elements as they exist on the page, View Transitions API does something quite unique in that it takes an image snapshot of the previous page state, and crossfades it with a live view of the new page state. For shared elements, it does the same thing, taking little image snapshots and then crossfading those with a live view of the element's new state. This can be leveraged to create interesting effects like full-screen wipes that aren't really in the scope of layout animations. Framer's Page Effects were built with the View Transitions API and it also extensively uses layout animations. The right tool for the right job. ### Drawbacks to View Transitions API There are quite a few drawbacks to the API vs layout animations: * **Not interruptible**: Interrupting an animation mid-way will snap the animation to the end before starting the next one. This feels very janky. * **Blocks interaction**: The animating elements overlay the "real" page underneath and block pointer events. Makes things feel quite sticky. * **Difficult to manage IDs**: Layout animations allow more than one element with a `layoutId` whereas View Transitions will break if the previous element isn't removed. * **Less performant:** View Transitions take an actual screenshot and animate via `width`/`height` vs layout animation's `transform`. This is measurably less performant when animating many elements. * **Doesn't account for scroll**: If the page scroll changes during a view transition, elements will incorrectly animate this delta. * **No relative animations:** If a nested element has a `delay` it will get "left behind" when its parent animates away, whereas Motion handles this kind of relative animation. * **One animation at a time**: View Transitions animate the whole screen, which means combining it with other animations is difficult and other view animations impossible. All-in-all, each system offers something different and each might be a better fit for your needs. In the future it might be that Motion also offers an API based on View Transitions API. Motion's industry-leading layout animations makes it easy to animate between any two layouts, even between different elements. It's as simple as a `layout` prop. <motion.div layout /> This little prop can animate previously unanimatable CSS values, like switching `justify-content` between `flex-start` and `flex-end`. <motion.div layout :style\="{ justifyContent: isOn ? 'flex-start' : 'flex-end' }" /> Or by using the `layoutId` prop, it's possible to match two elements and animate between them for some truly advanced animations. <motion.li layoutId\="item" /> It can handle anything from microinteractions to full page transitions. ## Usage Any layout change that happens as a result of a Vue re-render can be animated. <motion.div layout :style\="{ width: isOpen ? '80vw' : 0 }" /> Note that CSS changes should happen immediately via `style`, not `animate`, as `layout` will take care of the animation. Layout changes can be anything, changing `width`/`height`, number of grid columns, reordering a list, or adding/removing new items: ### Shared layout animations When a new component is added that has a `layoutId` prop that matches an existing component, it will automatically animate out from the old component. <motion.div v-if\="isSelected" layoutId\="underline" /> If the old component is still mounted when the new component enters, they will automatically crossfade from the old to the new. When removing an element to animate back to its origin layout, `AnimatePresence` can be used to keep it in the DOM until its exit animation has finished. <AnimatePresence\> <motion.div v-if\="isOpen" layoutId\="modal" /> </AnimatePresence\> ### Setting a transition Layout animations can be customised using the `transition` prop. <motion.div layout :transition\="{ duration: 0.3 }" /> If you want to set a transition specifically for **only** the layout animation, you can specify a specific `layout` transition. <motion.div layout :animate\="{ opacity: 0.5 }" :transition\="{ default: { ease: 'linear' }, layout: { duration: 0.3 } }" /> When performing a shared layout animation, the transition defined for element we're animating **to** will be used. <motion.button layoutId\="modal" @click\="() \=> isOn\=true" // This transition will be used when the modal closes :transition="{ type: 'spring' }" > Open </motion.button\> <AnimatePresence\> <motion.dialog v-if\="isOn" layoutId\="modal" // This transition will be used when the modal opens :transition="{ duration: 0.3 }" /> </AnimatePresence\> ### Animating within scrollable element To correctly animate layout within scrollable elements, we need to provide them the `layoutScroll` prop. <motion.div layoutScroll :style\="{ overflow: 'scroll' }" /> This lets Motion account for the element's scroll offset when measuring children. ### Animating within fixed containers To correctly animate layout within fixed elements, we need to provide them the `layoutRoot` prop. <motion.div layoutRoot :style\="{ position: 'fixed' }" /> This lets Motion account for the page's scroll offset when measuring children. ### Group layout animations Layout animations are triggered when a component re-renders and its layout has changed. <script setup\> const open = ref(false) </script\> <template\> <motion.div layout :style\="{ height: isOpen ? "100px" : "500px" }" @click="() => open=!open" /> </template\> But what happens when we have two or more components that don't re-render at the same time, but **do** affect each other's layout? <template\> <Accordion /> <Accordion /> </template\> When one re-renders, for performance reasons the other won't be able to detect changes to its layout. We can synchronise layout changes across multiple components by wrapping them in the `LayoutGroup component`. <script setup\> import { LayoutGroup } from "motion-v" </script\> <template\> <LayoutGroup\> <Accordion /> <Accordion /> </LayoutGroup\> </template\> When layout changes are detected in any grouped `motion` component, layout animations will trigger across all of them. ### Scale correction All layout animations are performed using the `transform` style, resulting in smooth framerates. Animating layout using transforms can sometimes visually distort children. To correct this distortion, the first child elements of the element can also be given `layout` property. Open this sandbox and try removing `layout` from the pink dot to see the difference this will make: Transforms can also distort `boxShadow` and `borderRadius`. The `motion` component will automatically correct this distortion on both props, as long as they're set as motion values. If you're not animating these values, the easiest way to do this is to set them via `style`. <motion.div layout :style\="{ borderRadius: 20 }" /> ## Troubleshooting ### The component isn't animating Ensure the component is **not** set to `display: inline`, as browsers don't apply `transform` to these elements. Ensure the component is re-rendering when you expect the layout animation to start. ### SVG layout animations are broken SVG components aren't currently supported with layout animations. SVGs don't have layout systems so it's recommended to directly animate their attributes like `cx` etc. ### The content stretches undesirably This is a natural side-effect of animating `width` and `height` with `scale`. Often, this can be fixed by providing these elements a `layout` animation and they'll be scale-corrected. <motion.section layout\> <motion.img layout /> </motion.section\> Some elements, like images or text that are changing between different aspect ratios, might be better animated with `layout="position"`. ### Border radius or box shadows are behaving strangely Animating `scale` is performant but can distort some styles like `border-radius` and `box-shadow`. Motion automatically corrects for scale distortion on these properties, but they must be set on the element via `style`. <motion.div layout :style\="{ borderRadius: '20px' }" /> ### Border looks stretched during animation Elements with a `border` may look stretched during the animation. This is for two reasons: 1. Because changing `border` triggers layout recalculations, it defeats the performance benefits of animating via `transform`. You might as well animate `width` and `height` classically. 2. `border` can't render smaller than `1px`, which limits the degree of scale correction that Motion can perform on this style. A work around is to replace `border` with a parent element with padding that acts as a `border`. <motion.div layout :style\="{ borderRadius: '10px', padding: '5px' }"\> <motion.div layout "style="{ borderRadius: '5px' }" /> </motion.div\> ## Technical reading Interested in the technical details behind layout animations? Nanda does an incredible job of explaining the challenges of animating layout with transforms using interactive examples. Matt, creator of Motion, did a talk at Vercel conference about the implementation details that is largely up to date. ## Differences with the View Transitions API More browsers are starting to support the View Transitions API, which is similar to Motion's layout animations. ### Benefits of View Transitions API The main two benefits of View Transitions is that **it's included in browsers** and **features a unique rendering system**. #### Filesize Because the View Transitions API is already included in browsers, it's cheap to implement very simple crossfade animations. However, the CSS complexity can scale quite quickly. Motion's layout animations are around 12kb but from there it's very cheap to change transitions, add springs, mark matching #### Rendering Whereas Motion animates the elements as they exist on the page, View Transitions API does something quite unique in that it takes an image snapshot of the previous page state, and crossfades it with a live view of the new page state. For shared elements, it does the same thing, taking little image snapshots and then crossfading those with a live view of the element's new state. This can be leveraged to create interesting effects like full-screen wipes that aren't really in the scope of layout animations. Framer's Page Effects were built with the View Transitions API and it also extensively uses layout animations. The right tool for the right job. ### Drawbacks to View Transitions API There are quite a few drawbacks to the API vs layout animations: * **Not interruptible**: Interrupting an animation mid-way will snap the animation to the end before starting the next one. This feels very janky. * **Blocks interaction**: The animating elements overlay the "real" page underneath and block pointer events. Makes things feel quite sticky. * **Difficult to manage IDs**: Layout animations allow more than one element with a `layoutId` whereas View Transitions will break if the previous element isn't removed. * **Less performant:** View Transitions take an actual screenshot and animate via `width`/`height` vs layout animation's `transform`. This is measurably less performant when animating many elements. * **Doesn't account for scroll**: If the page scroll changes during a view transition, elements will incorrectly animate this delta. * **No relative animations:** If a nested element has a `delay` it will get "left behind" when its parent animates away, whereas Motion handles this kind of relative animation. * **One animation at a time**: View Transitions animate the whole screen, which means combining it with other animations is difficult and other view animations impossible. All-in-all, each system offers something different and each might be a better fit for your needs. In the future it might be that Motion also offers an API based on View Transitions API. Motion's industry-leading layout animations makes it easy to animate between any two layouts, even between different elements. It's as simple as a `layout` prop. <motion.div layout /> This little prop can animate previously unanimatable CSS values, like switching `justify-content` between `flex-start` and `flex-end`. <motion.div layout :style\="{ justifyContent: isOn ? 'flex-start' : 'flex-end' }" /> Or by using the `layoutId` prop, it's possible to match two elements and animate between them for some truly advanced animations. <motion.li layoutId\="item" /> It can handle anything from microinteractions to full page transitions. ## Usage Any layout change that happens as a result of a Vue re-render can be animated. <motion.div layout :style\="{ width: isOpen ? '80vw' : 0 }" /> Note that CSS changes should happen immediately via `style`, not `animate`, as `layout` will take care of the animation. Layout changes can be anything, changing `width`/`height`, number of grid columns, reordering a list, or adding/removing new items: ### Shared layout animations When a new component is added that has a `layoutId` prop that matches an existing component, it will automatically animate out from the old component. <motion.div v-if\="isSelected" layoutId\="underline" /> If the old component is still mounted when the new component enters, they will automatically crossfade from the old to the new. When removing an element to animate back to its origin layout, `AnimatePresence` can be used to keep it in the DOM until its exit animation has finished. <AnimatePresence\> <motion.div v-if\="isOpen" layoutId\="modal" /> </AnimatePresence\> ### Setting a transition Layout animations can be customised using the `transition` prop. <motion.div layout :transition\="{ duration: 0.3 }" /> If you want to set a transition specifically for **only** the layout animation, you can specify a specific `layout` transition. <motion.div layout :animate\="{ opacity: 0.5 }" :transition\="{ default: { ease: 'linear' }, layout: { duration: 0.3 } }" /> When performing a shared layout animation, the transition defined for element we're animating **to** will be used. <motion.button layoutId\="modal" @click\="() \=> isOn\=true" // This transition will be used when the modal closes :transition="{ type: 'spring' }" > Open </motion.button\> <AnimatePresence\> <motion.dialog v-if\="isOn" layoutId\="modal" // This transition will be used when the modal opens :transition="{ duration: 0.3 }" /> </AnimatePresence\> ### Animating within scrollable element To correctly animate layout within scrollable elements, we need to provide them the `layoutScroll` prop. <motion.div layoutScroll :style\="{ overflow: 'scroll' }" /> This lets Motion account for the element's scroll offset when measuring children. ### Animating within fixed containers To correctly animate layout within fixed elements, we need to provide them the `layoutRoot` prop. <motion.div layoutRoot :style\="{ position: 'fixed' }" /> This lets Motion account for the page's scroll offset when measuring children. ### Group layout animations Layout animations are triggered when a component re-renders and its layout has changed. <script setup\> const open = ref(false) </script\> <template\> <motion.div layout :style\="{ height: isOpen ? "100px" : "500px" }" @click="() => open=!open" /> </template\> But what happens when we have two or more components that don't re-render at the same time, but **do** affect each other's layout? <template\> <Accordion /> <Accordion /> </template\> When one re-renders, for performance reasons the other won't be able to detect changes to its layout. We can synchronise layout changes across multiple components by wrapping them in the `LayoutGroup component`. <script setup\> import { LayoutGroup } from "motion-v" </script\> <template\> <LayoutGroup\> <Accordion /> <Accordion /> </LayoutGroup\> </template\> When layout changes are detected in any grouped `motion` component, layout animations will trigger across all of them. ### Scale correction All layout animations are performed using the `transform` style, resulting in smooth framerates. Animating layout using transforms can sometimes visually distort children. To correct this distortion, the first child elements of the element can also be given `layout` property. Open this sandbox and try removing `layout` from the pink dot to see the difference this will make: Transforms can also distort `boxShadow` and `borderRadius`. The `motion` component will automatically correct this distortion on both props, as long as they're set as motion values. If you're not animating these values, the easiest way to do this is to set them via `style`. <motion.div layout :style\="{ borderRadius: 20 }" /> ## Troubleshooting ### The component isn't animating Ensure the component is **not** set to `display: inline`, as browsers don't apply `transform` to these elements. Ensure the component is re-rendering when you expect the layout animation to start. ### SVG layout animations are broken SVG components aren't currently supported with layout animations. SVGs don't have layout systems so it's recommended to directly animate their attributes like `cx` etc. ### The content stretches undesirably This is a natural side-effect of animating `width` and `height` with `scale`. Often, this can be fixed by providing these elements a `layout` animation and they'll be scale-corrected. <motion.section layout\> <motion.img layout /> </motion.section\> Some elements, like images or text that are changing between different aspect ratios, might be better animated with `layout="position"`. ### Border radius or box shadows are behaving strangely Animating `scale` is performant but can distort some styles like `border-radius` and `box-shadow`. Motion automatically corrects for scale distortion on these properties, but they must be set on the element via `style`. <motion.div layout :style\="{ borderRadius: '20px' }" /> ### Border looks stretched during animation Elements with a `border` may look stretched during the animation. This is for two reasons: 1. Because changing `border` triggers layout recalculations, it defeats the performance benefits of animating via `transform`. You might as well animate `width` and `height` classically. 2. `border` can't render smaller than `1px`, which limits the degree of scale correction that Motion can perform on this style. A work around is to replace `border` with a parent element with padding that acts as a `border`. <motion.div layout :style\="{ borderRadius: '10px', padding: '5px' }"\> <motion.div layout "style="{ borderRadius: '5px' }" /> </motion.div\> ## Technical reading Interested in the technical details behind layout animations? Nanda does an incredible job of explaining the challenges of animating layout with transforms using interactive examples. Matt, creator of Motion, did a talk at Vercel conference about the implementation details that is largely up to date. ## Differences with the View Transitions API More browsers are starting to support the View Transitions API, which is similar to Motion's layout animations. ### Benefits of View Transitions API The main two benefits of View Transitions is that **it's included in browsers** and **features a unique rendering system**. #### Filesize Because the View Transitions API is already included in browsers, it's cheap to implement very simple crossfade animations. However, the CSS complexity can scale quite quickly. Motion's layout animations are around 12kb but from there it's very cheap to change transitions, add springs, mark matching #### Rendering Whereas Motion animates the elements as they exist on the page, View Transitions API does something quite unique in that it takes an image snapshot of the previous page state, and crossfades it with a live view of the new page state. For shared elements, it does the same thing, taking little image snapshots and then crossfading those with a live view of the element's new state. This can be leveraged to create interesting effects like full-screen wipes that aren't really in the scope of layout animations. Framer's Page Effects were built with the View Transitions API and it also extensively uses layout animations. The right tool for the right job. ### Drawbacks to View Transitions API There are quite a few drawbacks to the API vs layout animations: * **Not interruptible**: Interrupting an animation mid-way will snap the animation to the end before starting the next one. This feels very janky. * **Blocks interaction**: The animating elements overlay the "real" page underneath and block pointer events. Makes things feel quite sticky. * **Difficult to manage IDs**: Layout animations allow more than one element with a `layoutId` whereas View Transitions will break if the previous element isn't removed. * **Less performant:** View Transitions take an actual screenshot and animate via `width`/`height` vs layout animation's `transform`. This is measurably less performant when animating many elements. * **Doesn't account for scroll**: If the page scroll changes during a view transition, elements will incorrectly animate this delta. * **No relative animations:** If a nested element has a `delay` it will get "left behind" when its parent animates away, whereas Motion handles this kind of relative animation. * **One animation at a time**: View Transitions animate the whole screen, which means combining it with other animations is difficult and other view animations impossible. All-in-all, each system offers something different and each might be a better fit for your needs. In the future it might be that Motion also offers an API based on View Transitions API. --- ## Page: https://motion.dev/docs/vue-transitions # Transitions A `transition` defines the type of animation used when animating between two values. const transition = { duration: 0.8, delay: 0.5, ease: \[0, 0.71, 0.2, 1.01\], } // Motion component <motion.div :animate\="{ x: 100 }" :transition\="transition" /> // animate() function animate(".box", { x: 100 }, transition) ## Setting a transition `transition` can be set on any animation prop, and that transition will be used when the animation fires. <motion.div :whileHover\="{ scale: 1.1, transition: { duration: 0.2 } }" /> ### Value-specific transitions When animating multiple values, each value can be animated with a different transition, with `default` handling all other values: // Motion component <motion.li :animate\="{ x: 0, opacity: 1, transition: { default: { type: 'spring' }, opacity: { ease: 'linear' } } }" /> // animate() function animate("li", { x: 0, opacity: 1 }, { default: { type: "spring" }, opacity: { ease: "linear" } }) ### Default transitions It's possible to set default transitions via the `transition` prop. Either for specific `motion` components: <motion.div :animate\="{ x: 100 }" :transition\="{ type: 'spring', stiffness: 100 }" /> Or for a group of `motion` components via `MotionConfig`: <MotionConfig :transition\="{ duration: 0.4, ease: 'easeInOut' }"\> <App /> </MotionConfig\> ## Transition settings #### `type` **Default:** Dynamic `type` decides the type of animation to use. It can be `"tween"`, `"spring"` or `"inertia"`. **Tween** animations are set with a duration and an easing curve. **Spring** animations are either physics-based or duration-based. Physics-based spring animations are set via `stiffness`, `damping` and `mass`, and these incorporate the velocity of any existing gestures or animations for natural feedback. Duration-based spring animations are set via a `duration` and `bounce`. These don't incorporate velocity but are easier to understand. **Inertia** animations decelerate a value based on its initial velocity, usually used to implement inertial scrolling. <motion.path :animate\="{ pathLength: 1 }" :transition\="{ duration: 2, type: 'tween' }" /> #### Spring visualiser TimePhysics Duration Bounce Use visual duration ### Tween #### `duration` **Default:** `0.3` (or `0.8` if multiple keyframes are defined) The duration of the animation. Can also be used for `"spring"` animations when `bounce` is also set. animate("ul > li", { opacity: 1 }, { duration: 1 }) #### `ease` The easing function to use with tween animations. Accepts: * Easing function name. E.g `"linear"` * An array of four numbers to define a cubic bezier curve. E.g `[.17,.67,.83,.67]` * A JavaScript easing function, that accepts and returns a value `0`\-`1`. These are the available easing function names: * `"linear"` * `"easeIn"`, `"easeOut"`, `"easeInOut"` * `"circIn"`, `"circOut"`, `"circInOut"` * `"backIn"`, `"backOut"`, `"backInOut"` * `"anticipate"` When animating keyframes, `ease` can optionally be set as an array of easing functions to set different easings between each value: <motion.div :animate\="{ x: \[0, 100, 0\], transition: { ease: \['easeIn', 'easeOut'\] } }" /> > _I usually use the_ `_"easeOut"_` _curve for enter and exit transitions. The acceleration at the beginning gives the user a feeling of responsiveness. I use a duration no longer than_ `_0.3_`_/_`_0.4_` _seconds to keep the animation fast._~ Emil Kowalski, Animations on the Web #### `times` When animating multiple keyframes, `times` can be used to adjust the position of each keyframe throughout the animation. Each value in `times` is a value between `0` and `1`, representing the start and end of the animation. <motion.div :animate\="{ x: \[0, 100, 0\], transition: { times: \[0, 0.3, 1\] } }" /> There must be the same number of `times` as there are keyframes. Defaults to an array of evenly-spread durations. ### Spring #### `bounce` **Default:** `0.25` `bounce` determines the "bounciness" of a spring animation. `0` is no bounce, and `1` is extremely bouncy. **Note:** `bounce` and `duration` will be overridden if `stiffness`, `damping` or `mass` are set. <motion.div :animate\="{ rotateX: 90 }" :transition\="{ type: 'spring', bounce: 0.25 }" /> #### `visualDuration` If `visualDuration` is set, this will override `duration`. The visual duration is a time, **set in seconds**, that the animation will take to visually appear to reach its target. In other words, the bulk of the transition will occur before this time, and the "bouncy bit" will mostly happen after. This makes it easier to edit a spring, as well as visually coordinate it with other time-based animations. <motion.div :animate\="{ rotateX: 90 }" :transition\="{ type: 'spring', visualDuration: 0.5, bounce: 0.25 }" /> #### `damping` **Default:** `10` Strength of opposing force. If set to 0, spring will oscillate indefinitely. <motion.a :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', damping: 300 }" /> #### `mass` **Default:** `1` Mass of the moving object. Higher values will result in more lethargic movement. <motion.feTurbulence :animate\="{ baseFrequency: 0.5 }" :transition\="{ type: 'spring', mass: 0.5 }" /> #### `stiffness` **Default:** `1` Stiffness of the spring. Higher values will create more sudden movement. <motion.section animate\={{ rotate: 180 }} transition\={{ type: 'spring', stiffness: 50 }} /> > _I never really understood how_ `_damping_`_,_ `_mass_` _and_ `_stiffness_` _influence a spring animation, so I made a_ _tool for myself_ _to visualise it._~ Emil Kowalski, Animations on the Web #### `velocity` **Default:** Current value velocity The initial velocity of the spring. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', velocity: 2 }" /> #### `restSpeed` **Default:** `0.1` End animation if absolute speed (in units per second) drops below this value and delta is smaller than `restDelta`. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', restSpeed: 0.5 }" /> #### `restDelta` **Default:** `0.01` End animation if distance is below this value and speed is below `restSpeed`. When animation ends, the spring will end. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', restDelta: 0.5 }" /> ### Inertia An animation that decelerates a value based on its initial velocity. Optionally, `min` and `max` boundaries can be defined, and inertia will snap to these with a spring animation. This animation will automatically precalculate a target value, which can be modified with the `modifyTarget` property. This allows you to add snap-to-grid or similar functionality. Inertia is also the animation used for `dragTransition`, and can be configured via that prop. #### `power` **Default:** `0.8` A higher power value equals a further calculated target. <motion.div drag :dragTransition\="{ power: 0.2 }" /> #### `timeConstant` **Default:** `**700**` Adjusting the time constant will change the duration of the deceleration, thereby affecting its feel. <motion.div drag :dragTransition\="{ timeConstant: 200 }" /> #### `modifyTarget` A function that receives the automatically-calculated target and returns a new one. Useful for snapping the target to a grid. <motion.div drag // dragTransition always type: inertia :dragTransition\="{ power: 0, // Snap calculated target to nearest 50 pixels modifyTarget: target => Math.round(target / 50) \* 50 }" /> #### `min` Minimum constraint. If set, the value will "bump" against this value (or immediately spring to it if the animation starts as less than this value). <motion.div drag :dragTransition\="{ min: 0, max: 100 }" /> #### `max` Maximum constraint. If set, the value will "bump" against this value (or immediately snap to it, if the initial animation value exceeds this value). <motion.div drag :dragTransition\="{ min: 0, max: 100 }" /> #### `bounceStiffness` **Default:** `500` If `min` or `max` is set, this affects the stiffness of the bounce spring. Higher values will create more sudden movement. <motion.div drag :dragTransition\="{ min: 0, max: 100, bounceStiffness: 100 }" /> #### `bounceDamping` **Default:** `10` If `min` or `max` is set, this affects the damping of the bounce spring. If set to `0`, spring will oscillate indefinitely. <motion.div drag :dragTransition\="{ min: 0, max: 100, bounceStiffness: 100 }" /> ### Orchestration #### `delay` **Default:** `0` Delay the animation by this duration (in seconds). animate(element, { filter: "blur(10px)" }, { delay: 0.3 }) By setting `delay` to a negative value, the animation will start that long into the animation. For instance to start 1 second in, `delay` can be set to -`1`. #### `repeat` **Default:** `0` The number of times to repeat the transition. Set to `Infinity` for perpetual animation. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: Infinity, duration: 2 }" /> #### `repeatType` **Default:** `"loop"` How to repeat the animation. This can be either: * `loop`: Repeats the animation from the start. * `reverse`: Alternates between forward and backwards playback. * `mirror`: Switches animation origin and target on each iteration. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: 1, repeatType: 'reverse', duration: 2 }" /> #### `repeatDelay` **Default:** `0` When repeating an animation, `repeatDelay` will set the duration of the time to wait, in seconds, between each repetition. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: Infinity, repeatDelay: 1 }" /> #### `when` **Default:** `false` With variants, describes when an animation should trigger, relative to that of its children. * `"beforeChildren"`: Children animations will play after the parent animation finishes. * `"afterChildren"`: Parent animations will play after the children animations finish. <script setup\> const list = { hidden: { opacity: 0, transition: { when: "afterChildren" } } } const item = { hidden: { opacity: 0, transition: { duration: 2 } } } </script\> <template\> <motion.ul :variants\="list" animate\="hidden"\> <motion.li :variants\="item" /> <motion.li :variants\="item" /> </motion.ul\> </template\> #### `delayChildren` **Default:** `0` With variants, setting `delayChildren` on a parent will delay child animations by this duration (in seconds). <sctipt setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { delayChildren: 0.5 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </sctipt\> <template\> <motion.ul :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" /> <motion.li :variants\="item"/> </motion.ul\> </template\> #### `staggerChildren` **Default:** `0` With variants, setting `staggerChildren` on a parent will stagger children by this duration. For example, if `staggerChildren` is set to `0.1`, the first child will delayed by `0` seconds, the second by `0.1`, the third by `0.2` etc. The calculated delay will be additional to `delayChildren`. <sctipt setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.5 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </sctipt\> <template\> <motion.ol :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" /> <motion.li :variants\="item" /> </motion.ol\> </template\> #### `staggerDirection` **Default:** `1` The direction in which to stagger children. `1` will stagger from the first to last child, while `-1` staggers from last to first. <script setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { delayChildren: 0.5, staggerDirection: -1 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </script\> <template\> <motion.ul :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" :size\="50" /> <motion.li :variants\="item" :size\="50" /> </motion.ul\> </template\> A `transition` defines the type of animation used when animating between two values. const transition = { duration: 0.8, delay: 0.5, ease: \[0, 0.71, 0.2, 1.01\], } // Motion component <motion.div :animate\="{ x: 100 }" :transition\="transition" /> // animate() function animate(".box", { x: 100 }, transition) ## Setting a transition `transition` can be set on any animation prop, and that transition will be used when the animation fires. <motion.div :whileHover\="{ scale: 1.1, transition: { duration: 0.2 } }" /> ### Value-specific transitions When animating multiple values, each value can be animated with a different transition, with `default` handling all other values: // Motion component <motion.li :animate\="{ x: 0, opacity: 1, transition: { default: { type: 'spring' }, opacity: { ease: 'linear' } } }" /> // animate() function animate("li", { x: 0, opacity: 1 }, { default: { type: "spring" }, opacity: { ease: "linear" } }) ### Default transitions It's possible to set default transitions via the `transition` prop. Either for specific `motion` components: <motion.div :animate\="{ x: 100 }" :transition\="{ type: 'spring', stiffness: 100 }" /> Or for a group of `motion` components via `MotionConfig`: <MotionConfig :transition\="{ duration: 0.4, ease: 'easeInOut' }"\> <App /> </MotionConfig\> ## Transition settings #### `type` **Default:** Dynamic `type` decides the type of animation to use. It can be `"tween"`, `"spring"` or `"inertia"`. **Tween** animations are set with a duration and an easing curve. **Spring** animations are either physics-based or duration-based. Physics-based spring animations are set via `stiffness`, `damping` and `mass`, and these incorporate the velocity of any existing gestures or animations for natural feedback. Duration-based spring animations are set via a `duration` and `bounce`. These don't incorporate velocity but are easier to understand. **Inertia** animations decelerate a value based on its initial velocity, usually used to implement inertial scrolling. <motion.path :animate\="{ pathLength: 1 }" :transition\="{ duration: 2, type: 'tween' }" /> #### Spring visualiser TimePhysics Duration Bounce Use visual duration ### Tween #### `duration` **Default:** `0.3` (or `0.8` if multiple keyframes are defined) The duration of the animation. Can also be used for `"spring"` animations when `bounce` is also set. animate("ul > li", { opacity: 1 }, { duration: 1 }) #### `ease` The easing function to use with tween animations. Accepts: * Easing function name. E.g `"linear"` * An array of four numbers to define a cubic bezier curve. E.g `[.17,.67,.83,.67]` * A JavaScript easing function, that accepts and returns a value `0`\-`1`. These are the available easing function names: * `"linear"` * `"easeIn"`, `"easeOut"`, `"easeInOut"` * `"circIn"`, `"circOut"`, `"circInOut"` * `"backIn"`, `"backOut"`, `"backInOut"` * `"anticipate"` When animating keyframes, `ease` can optionally be set as an array of easing functions to set different easings between each value: <motion.div :animate\="{ x: \[0, 100, 0\], transition: { ease: \['easeIn', 'easeOut'\] } }" /> > _I usually use the_ `_"easeOut"_` _curve for enter and exit transitions. The acceleration at the beginning gives the user a feeling of responsiveness. I use a duration no longer than_ `_0.3_`_/_`_0.4_` _seconds to keep the animation fast._~ Emil Kowalski, Animations on the Web #### `times` When animating multiple keyframes, `times` can be used to adjust the position of each keyframe throughout the animation. Each value in `times` is a value between `0` and `1`, representing the start and end of the animation. <motion.div :animate\="{ x: \[0, 100, 0\], transition: { times: \[0, 0.3, 1\] } }" /> There must be the same number of `times` as there are keyframes. Defaults to an array of evenly-spread durations. ### Spring #### `bounce` **Default:** `0.25` `bounce` determines the "bounciness" of a spring animation. `0` is no bounce, and `1` is extremely bouncy. **Note:** `bounce` and `duration` will be overridden if `stiffness`, `damping` or `mass` are set. <motion.div :animate\="{ rotateX: 90 }" :transition\="{ type: 'spring', bounce: 0.25 }" /> #### `visualDuration` If `visualDuration` is set, this will override `duration`. The visual duration is a time, **set in seconds**, that the animation will take to visually appear to reach its target. In other words, the bulk of the transition will occur before this time, and the "bouncy bit" will mostly happen after. This makes it easier to edit a spring, as well as visually coordinate it with other time-based animations. <motion.div :animate\="{ rotateX: 90 }" :transition\="{ type: 'spring', visualDuration: 0.5, bounce: 0.25 }" /> #### `damping` **Default:** `10` Strength of opposing force. If set to 0, spring will oscillate indefinitely. <motion.a :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', damping: 300 }" /> #### `mass` **Default:** `1` Mass of the moving object. Higher values will result in more lethargic movement. <motion.feTurbulence :animate\="{ baseFrequency: 0.5 }" :transition\="{ type: 'spring', mass: 0.5 }" /> #### `stiffness` **Default:** `1` Stiffness of the spring. Higher values will create more sudden movement. <motion.section animate\={{ rotate: 180 }} transition\={{ type: 'spring', stiffness: 50 }} /> > _I never really understood how_ `_damping_`_,_ `_mass_` _and_ `_stiffness_` _influence a spring animation, so I made a_ _tool for myself_ _to visualise it._~ Emil Kowalski, Animations on the Web #### `velocity` **Default:** Current value velocity The initial velocity of the spring. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', velocity: 2 }" /> #### `restSpeed` **Default:** `0.1` End animation if absolute speed (in units per second) drops below this value and delta is smaller than `restDelta`. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', restSpeed: 0.5 }" /> #### `restDelta` **Default:** `0.01` End animation if distance is below this value and speed is below `restSpeed`. When animation ends, the spring will end. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', restDelta: 0.5 }" /> ### Inertia An animation that decelerates a value based on its initial velocity. Optionally, `min` and `max` boundaries can be defined, and inertia will snap to these with a spring animation. This animation will automatically precalculate a target value, which can be modified with the `modifyTarget` property. This allows you to add snap-to-grid or similar functionality. Inertia is also the animation used for `dragTransition`, and can be configured via that prop. #### `power` **Default:** `0.8` A higher power value equals a further calculated target. <motion.div drag :dragTransition\="{ power: 0.2 }" /> #### `timeConstant` **Default:** `**700**` Adjusting the time constant will change the duration of the deceleration, thereby affecting its feel. <motion.div drag :dragTransition\="{ timeConstant: 200 }" /> #### `modifyTarget` A function that receives the automatically-calculated target and returns a new one. Useful for snapping the target to a grid. <motion.div drag // dragTransition always type: inertia :dragTransition\="{ power: 0, // Snap calculated target to nearest 50 pixels modifyTarget: target => Math.round(target / 50) \* 50 }" /> #### `min` Minimum constraint. If set, the value will "bump" against this value (or immediately spring to it if the animation starts as less than this value). <motion.div drag :dragTransition\="{ min: 0, max: 100 }" /> #### `max` Maximum constraint. If set, the value will "bump" against this value (or immediately snap to it, if the initial animation value exceeds this value). <motion.div drag :dragTransition\="{ min: 0, max: 100 }" /> #### `bounceStiffness` **Default:** `500` If `min` or `max` is set, this affects the stiffness of the bounce spring. Higher values will create more sudden movement. <motion.div drag :dragTransition\="{ min: 0, max: 100, bounceStiffness: 100 }" /> #### `bounceDamping` **Default:** `10` If `min` or `max` is set, this affects the damping of the bounce spring. If set to `0`, spring will oscillate indefinitely. <motion.div drag :dragTransition\="{ min: 0, max: 100, bounceStiffness: 100 }" /> ### Orchestration #### `delay` **Default:** `0` Delay the animation by this duration (in seconds). animate(element, { filter: "blur(10px)" }, { delay: 0.3 }) By setting `delay` to a negative value, the animation will start that long into the animation. For instance to start 1 second in, `delay` can be set to -`1`. #### `repeat` **Default:** `0` The number of times to repeat the transition. Set to `Infinity` for perpetual animation. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: Infinity, duration: 2 }" /> #### `repeatType` **Default:** `"loop"` How to repeat the animation. This can be either: * `loop`: Repeats the animation from the start. * `reverse`: Alternates between forward and backwards playback. * `mirror`: Switches animation origin and target on each iteration. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: 1, repeatType: 'reverse', duration: 2 }" /> #### `repeatDelay` **Default:** `0` When repeating an animation, `repeatDelay` will set the duration of the time to wait, in seconds, between each repetition. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: Infinity, repeatDelay: 1 }" /> #### `when` **Default:** `false` With variants, describes when an animation should trigger, relative to that of its children. * `"beforeChildren"`: Children animations will play after the parent animation finishes. * `"afterChildren"`: Parent animations will play after the children animations finish. <script setup\> const list = { hidden: { opacity: 0, transition: { when: "afterChildren" } } } const item = { hidden: { opacity: 0, transition: { duration: 2 } } } </script\> <template\> <motion.ul :variants\="list" animate\="hidden"\> <motion.li :variants\="item" /> <motion.li :variants\="item" /> </motion.ul\> </template\> #### `delayChildren` **Default:** `0` With variants, setting `delayChildren` on a parent will delay child animations by this duration (in seconds). <sctipt setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { delayChildren: 0.5 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </sctipt\> <template\> <motion.ul :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" /> <motion.li :variants\="item"/> </motion.ul\> </template\> #### `staggerChildren` **Default:** `0` With variants, setting `staggerChildren` on a parent will stagger children by this duration. For example, if `staggerChildren` is set to `0.1`, the first child will delayed by `0` seconds, the second by `0.1`, the third by `0.2` etc. The calculated delay will be additional to `delayChildren`. <sctipt setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.5 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </sctipt\> <template\> <motion.ol :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" /> <motion.li :variants\="item" /> </motion.ol\> </template\> #### `staggerDirection` **Default:** `1` The direction in which to stagger children. `1` will stagger from the first to last child, while `-1` staggers from last to first. <script setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { delayChildren: 0.5, staggerDirection: -1 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </script\> <template\> <motion.ul :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" :size\="50" /> <motion.li :variants\="item" :size\="50" /> </motion.ul\> </template\> A `transition` defines the type of animation used when animating between two values. const transition = { duration: 0.8, delay: 0.5, ease: \[0, 0.71, 0.2, 1.01\], } // Motion component <motion.div :animate\="{ x: 100 }" :transition\="transition" /> // animate() function animate(".box", { x: 100 }, transition) ## Setting a transition `transition` can be set on any animation prop, and that transition will be used when the animation fires. <motion.div :whileHover\="{ scale: 1.1, transition: { duration: 0.2 } }" /> ### Value-specific transitions When animating multiple values, each value can be animated with a different transition, with `default` handling all other values: // Motion component <motion.li :animate\="{ x: 0, opacity: 1, transition: { default: { type: 'spring' }, opacity: { ease: 'linear' } } }" /> // animate() function animate("li", { x: 0, opacity: 1 }, { default: { type: "spring" }, opacity: { ease: "linear" } }) ### Default transitions It's possible to set default transitions via the `transition` prop. Either for specific `motion` components: <motion.div :animate\="{ x: 100 }" :transition\="{ type: 'spring', stiffness: 100 }" /> Or for a group of `motion` components via `MotionConfig`: <MotionConfig :transition\="{ duration: 0.4, ease: 'easeInOut' }"\> <App /> </MotionConfig\> ## Transition settings #### `type` **Default:** Dynamic `type` decides the type of animation to use. It can be `"tween"`, `"spring"` or `"inertia"`. **Tween** animations are set with a duration and an easing curve. **Spring** animations are either physics-based or duration-based. Physics-based spring animations are set via `stiffness`, `damping` and `mass`, and these incorporate the velocity of any existing gestures or animations for natural feedback. Duration-based spring animations are set via a `duration` and `bounce`. These don't incorporate velocity but are easier to understand. **Inertia** animations decelerate a value based on its initial velocity, usually used to implement inertial scrolling. <motion.path :animate\="{ pathLength: 1 }" :transition\="{ duration: 2, type: 'tween' }" /> #### Spring visualiser TimePhysics Duration Bounce Use visual duration ### Tween #### `duration` **Default:** `0.3` (or `0.8` if multiple keyframes are defined) The duration of the animation. Can also be used for `"spring"` animations when `bounce` is also set. animate("ul > li", { opacity: 1 }, { duration: 1 }) #### `ease` The easing function to use with tween animations. Accepts: * Easing function name. E.g `"linear"` * An array of four numbers to define a cubic bezier curve. E.g `[.17,.67,.83,.67]` * A JavaScript easing function, that accepts and returns a value `0`\-`1`. These are the available easing function names: * `"linear"` * `"easeIn"`, `"easeOut"`, `"easeInOut"` * `"circIn"`, `"circOut"`, `"circInOut"` * `"backIn"`, `"backOut"`, `"backInOut"` * `"anticipate"` When animating keyframes, `ease` can optionally be set as an array of easing functions to set different easings between each value: <motion.div :animate\="{ x: \[0, 100, 0\], transition: { ease: \['easeIn', 'easeOut'\] } }" /> > _I usually use the_ `_"easeOut"_` _curve for enter and exit transitions. The acceleration at the beginning gives the user a feeling of responsiveness. I use a duration no longer than_ `_0.3_`_/_`_0.4_` _seconds to keep the animation fast._~ Emil Kowalski, Animations on the Web #### `times` When animating multiple keyframes, `times` can be used to adjust the position of each keyframe throughout the animation. Each value in `times` is a value between `0` and `1`, representing the start and end of the animation. <motion.div :animate\="{ x: \[0, 100, 0\], transition: { times: \[0, 0.3, 1\] } }" /> There must be the same number of `times` as there are keyframes. Defaults to an array of evenly-spread durations. ### Spring #### `bounce` **Default:** `0.25` `bounce` determines the "bounciness" of a spring animation. `0` is no bounce, and `1` is extremely bouncy. **Note:** `bounce` and `duration` will be overridden if `stiffness`, `damping` or `mass` are set. <motion.div :animate\="{ rotateX: 90 }" :transition\="{ type: 'spring', bounce: 0.25 }" /> #### `visualDuration` If `visualDuration` is set, this will override `duration`. The visual duration is a time, **set in seconds**, that the animation will take to visually appear to reach its target. In other words, the bulk of the transition will occur before this time, and the "bouncy bit" will mostly happen after. This makes it easier to edit a spring, as well as visually coordinate it with other time-based animations. <motion.div :animate\="{ rotateX: 90 }" :transition\="{ type: 'spring', visualDuration: 0.5, bounce: 0.25 }" /> #### `damping` **Default:** `10` Strength of opposing force. If set to 0, spring will oscillate indefinitely. <motion.a :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', damping: 300 }" /> #### `mass` **Default:** `1` Mass of the moving object. Higher values will result in more lethargic movement. <motion.feTurbulence :animate\="{ baseFrequency: 0.5 }" :transition\="{ type: 'spring', mass: 0.5 }" /> #### `stiffness` **Default:** `1` Stiffness of the spring. Higher values will create more sudden movement. <motion.section animate\={{ rotate: 180 }} transition\={{ type: 'spring', stiffness: 50 }} /> > _I never really understood how_ `_damping_`_,_ `_mass_` _and_ `_stiffness_` _influence a spring animation, so I made a_ _tool for myself_ _to visualise it._~ Emil Kowalski, Animations on the Web #### `velocity` **Default:** Current value velocity The initial velocity of the spring. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', velocity: 2 }" /> #### `restSpeed` **Default:** `0.1` End animation if absolute speed (in units per second) drops below this value and delta is smaller than `restDelta`. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', restSpeed: 0.5 }" /> #### `restDelta` **Default:** `0.01` End animation if distance is below this value and speed is below `restSpeed`. When animation ends, the spring will end. <motion.div :animate\="{ rotate: 180 }" :transition\="{ type: 'spring', restDelta: 0.5 }" /> ### Inertia An animation that decelerates a value based on its initial velocity. Optionally, `min` and `max` boundaries can be defined, and inertia will snap to these with a spring animation. This animation will automatically precalculate a target value, which can be modified with the `modifyTarget` property. This allows you to add snap-to-grid or similar functionality. Inertia is also the animation used for `dragTransition`, and can be configured via that prop. #### `power` **Default:** `0.8` A higher power value equals a further calculated target. <motion.div drag :dragTransition\="{ power: 0.2 }" /> #### `timeConstant` **Default:** `**700**` Adjusting the time constant will change the duration of the deceleration, thereby affecting its feel. <motion.div drag :dragTransition\="{ timeConstant: 200 }" /> #### `modifyTarget` A function that receives the automatically-calculated target and returns a new one. Useful for snapping the target to a grid. <motion.div drag // dragTransition always type: inertia :dragTransition\="{ power: 0, // Snap calculated target to nearest 50 pixels modifyTarget: target => Math.round(target / 50) \* 50 }" /> #### `min` Minimum constraint. If set, the value will "bump" against this value (or immediately spring to it if the animation starts as less than this value). <motion.div drag :dragTransition\="{ min: 0, max: 100 }" /> #### `max` Maximum constraint. If set, the value will "bump" against this value (or immediately snap to it, if the initial animation value exceeds this value). <motion.div drag :dragTransition\="{ min: 0, max: 100 }" /> #### `bounceStiffness` **Default:** `500` If `min` or `max` is set, this affects the stiffness of the bounce spring. Higher values will create more sudden movement. <motion.div drag :dragTransition\="{ min: 0, max: 100, bounceStiffness: 100 }" /> #### `bounceDamping` **Default:** `10` If `min` or `max` is set, this affects the damping of the bounce spring. If set to `0`, spring will oscillate indefinitely. <motion.div drag :dragTransition\="{ min: 0, max: 100, bounceStiffness: 100 }" /> ### Orchestration #### `delay` **Default:** `0` Delay the animation by this duration (in seconds). animate(element, { filter: "blur(10px)" }, { delay: 0.3 }) By setting `delay` to a negative value, the animation will start that long into the animation. For instance to start 1 second in, `delay` can be set to -`1`. #### `repeat` **Default:** `0` The number of times to repeat the transition. Set to `Infinity` for perpetual animation. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: Infinity, duration: 2 }" /> #### `repeatType` **Default:** `"loop"` How to repeat the animation. This can be either: * `loop`: Repeats the animation from the start. * `reverse`: Alternates between forward and backwards playback. * `mirror`: Switches animation origin and target on each iteration. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: 1, repeatType: 'reverse', duration: 2 }" /> #### `repeatDelay` **Default:** `0` When repeating an animation, `repeatDelay` will set the duration of the time to wait, in seconds, between each repetition. <motion.div :animate\="{ rotate: 180 }" :transition\="{ repeat: Infinity, repeatDelay: 1 }" /> #### `when` **Default:** `false` With variants, describes when an animation should trigger, relative to that of its children. * `"beforeChildren"`: Children animations will play after the parent animation finishes. * `"afterChildren"`: Parent animations will play after the children animations finish. <script setup\> const list = { hidden: { opacity: 0, transition: { when: "afterChildren" } } } const item = { hidden: { opacity: 0, transition: { duration: 2 } } } </script\> <template\> <motion.ul :variants\="list" animate\="hidden"\> <motion.li :variants\="item" /> <motion.li :variants\="item" /> </motion.ul\> </template\> #### `delayChildren` **Default:** `0` With variants, setting `delayChildren` on a parent will delay child animations by this duration (in seconds). <sctipt setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { delayChildren: 0.5 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </sctipt\> <template\> <motion.ul :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" /> <motion.li :variants\="item"/> </motion.ul\> </template\> #### `staggerChildren` **Default:** `0` With variants, setting `staggerChildren` on a parent will stagger children by this duration. For example, if `staggerChildren` is set to `0.1`, the first child will delayed by `0` seconds, the second by `0.1`, the third by `0.2` etc. The calculated delay will be additional to `delayChildren`. <sctipt setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.5 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </sctipt\> <template\> <motion.ol :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" /> <motion.li :variants\="item" /> </motion.ol\> </template\> #### `staggerDirection` **Default:** `1` The direction in which to stagger children. `1` will stagger from the first to last child, while `-1` staggers from last to first. <script setup\> const container = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { delayChildren: 0.5, staggerDirection: -1 } } } const item = { hidden: { opacity: 0 }, show: { opacity: 1 } } </script\> <template\> <motion.ul :variants\="container" initial\="hidden" animate\="show" \> <motion.li :variants\="item" :size\="50" /> <motion.li :variants\="item" :size\="50" /> </motion.ul\> </template\> --- ## Page: https://motion.dev/docs/vue-motion-component # motion The `motion` component drives most animations in Motion for Vue. There's a `motion` component for every HTML and SVG element, for instance `motion.div`, `motion.circle` etc. Think of it as a normal Vue component, supercharged for 120fps animation and gestures. ## Usage Import `motion` from `motion-v`: import { motion } from "motion-v" Now you can use it exactly as you would any normal HTML/SVG component: <motion.div class\="box" /> But you also gain access to powerful animation APIs like the `animate`, `layout`, `whileInView` props and much more. <motion.div class\="box" // Animate when this value changes: :animate\="{ scale: 2 }" // Fade in when the element enters the viewport: :whileInView\="{ opacity: 1 }" // Animate the component when its layout changes: layout // Style now supports indepedent transforms: :style\="{ x: 100 }" /> Check out the Animation guide for a full overview on animations in Motion for Vue. ### Performance `motion` components animate values outside the Vue render cycle for improved performance. Using motion values instead of Vue state to update `style` will also avoid re-renders. <script setup\> import { useMotionValue } from "motion-v" const x = useMotionValue(0) let timeout; onMounted(() \=> { // Won't trigger a re-render! timeout = setTimeout(() \=> x.set(100), 1000) }) onUnMounted(()\=>{ clearTimeout(timeout) }) </script\> <template\> <motion.div :style\="{ x }" /> </template\> ### Server-side rendering `motion` components are fully compatible with server-side rendering, meaning the initial state of the component will be reflected in the server-generated output. // Server will output \`translateX(100px)\` <motion.div :initial\="false" :animate\="{ x: 100 }" /> This is with the exception of some SVG attributes like `transform` which require DOM measurements to calculate. ### Custom components Any Vue component can be supercharged into a `motion` component by passing it to `motion.create()` as a function. const MotionComponent = motion.create(Component) **Important:** Make sure **not** to call `motion.create()` within template! This will make a new component every render, breaking your animations. It's also possible to pass strings to `motion.create`, which will create custom DOM elements. // Will render <custom-element /> into HTML const MotionComponent = motion.create('custom-element') By default, all `motion` props (like `animate` etc) are filtered out of the `props` forwarded to the provided component. By providing a `forwardMotionProps` config, the provided component will receive these props. motion.create(Component, { forwardMotionProps: true }) ## Props `motion` components accept the following props. ### Animation #### `initial` The initial visual state of the `motion` component. This can be set as an animation target: <motion.section :initial\="{ opacity: 0, x: 0 }" /> Variants: <motion.li initial\="visible" /> <motion.div :initial\="\['visible', 'active'\]" /> Or set as `false` to disable the enter animation and initially render as the values found in `animate`. <motion.div :initial\="false" :animate\="{ opacity: 0 }" /> #### `animate` A target to animate to on enter, and on update. Can be set as an animation target: <motion.div :initial\="{ boxShadow: '0px 0px #000' }" :animate\="{ boxShadow: '10px 10px #000' }" /> Or variants: <motion.li animate\="visible" /> <motion.div initial\="hidden" :animate\="\['visible', 'active'\]" /> #### `exit` A target to animate to when a component is removed from the tree. Can be set either as an animation target, or variant. **Note:** Owing to Vue Transition component limitations, the component being removed **must** be a **direct child** of `AnimatePresence` to enable this animation. <AnimatePresence\> <ul v-if\="isVisible" key\="list"\> <motion.li :exit\="{ opacity: 0 }" /> </ul\> </AnimatePresence\> #### `transition` The default transition for this component to use when an animation prop (`animate`, `whileHover` etc) has no `transition` defined. <motion.div :transition\="{ type: 'spring' }" :animate\="{ scale: 1.2 }" /> #### `variants` The variants for this component. <script setup\> const variants = { active: { backgroundColor: "#f00" }, inactive: { backgroundColor: "#fff", transition: { duration: 2 } } } </script\> <template\> <motion.div :variants\="variants" :animate\="isActive ? 'active' : 'inactive'" /> </template\> #### `style` The normal Vue DOM `style` prop, with added support for motion values and independent transforms. <script setup\> const x = useMotionValue(30) </script\> <tempalte\> <motion.div :style\="{ x, rotate: 90, originX: 0.5 }" /> </tempalte\> #### `onUpdate` Callback triggered every frame any value on the `motion` component updates. It's provided a single argument with the latest values. <motion.article :animate\="{ opacity: 1 }" :@update\="latest \=> console.log(latest.opacity)" /> #### `onAnimationStart` Callback triggered when any animation (except layout animations, see `onLayoutAnimationStart`) starts. It's provided a single argument, with the target or variant name of the started animation. <motion.circle :animate\="{ r: 10 }" @animationStart\="latest \=> console.log(latest.r)" /> #### `onAnimationComplete` Callback triggered when any animation (except layout animations, see `onLayoutAnimationComplete`) completes. It's provided a single argument, with the target or variant name of the completed animation. <motion.circle :animate\="{ r: 10 }" @animationComplete\="latest \=> console.log(latest.r)" /> ### Hover #### `whileHover` Target or variants to label to while the hover gesture is active. <!-- As target --> <motion.button :whileHover\="{ scale: 1.2 }" /> <!-- As variants --> <motion.div whileHover\="hovered" /> #### `onHoverStart` Callback function that fires when a pointer starts hovering over the component. Provided the triggering `PointerEvent`. <motion.div @hoverStart\="(event) \=> console.log(event)" /> #### `onHoverEnd` Callback function that fires when a pointer stops hovering over the component. Provided the triggering `PointerEvent`. <motion.div @hoverEnd\="(event) \=> console.log(event)" /> ### Press #### `whilePress` Target or variants to label to while the press gesture is active. <!-- // As target --> <motion.button :whilePress\="{ scale: 0.9 }" /> <!-- // As variants --> <motion.div whilePress\="tapped" /> #### `onPressStart` Callback function that fires when a pointer starts pressing the component. Provided the triggering `PointerEvent`. <motion.div @pressStart\="(event) \=> console.log(event)" /> #### `onPress` Callback function that fires when a pointer stops pressing the component and the pointer was released **inside** the component. Provided the triggering `PointerEvent`. <motion.div @press\="(event) \=> console.log(event)" /> #### `onPressCancel` Callback function that fires when a pointer stops pressing the component and the pointer was released **outside** the component. Provided the triggering `PointerEvent`. <motion.div @pressCancel\="(event) \=> console.log(event)" /> ### Focus #### `whileFocus` Target or variants to label to while the focus gesture is active. <!-- As target --> <motion.button :whileFocus\="{ outline: 'dashed #000' }" /> <!-- As variants --> <motion.div whileFocus\="focused" /> ### Pan #### `onPan` Callback function that fires when the pan gesture is recognised on this element. **Note:** For pan gestures to work correctly with touch input, the element needs touch scrolling to be disabled on either x/y or both axis with the `touch-action` CSS rule. function onPan(event, info) { console.log(info.point.x, info.point.y) } <motion.div @pan\="onPan" /> Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following: * `point`: Relative to the device or page. * `delta`: Distance since the last event. * `offset`: Distance from the original event. * `velocity`: Current velocity of the pointer. #### `onPanStart` Callback function that fires when a pan gesture starts. Provided the triggering `PointerEvent` and `info`. <motion.div @panStart\="(event, info) \=> console.log(info.delta.x)" /> #### `onPanEnd` Callback function that fires when a pan gesture ends. Provided the triggering `PointerEvent` and `info`. <motion.div @panEnd\="(event, info) \=> console.log(info.delta.x)" /> #### Drag #### `drag` **Default:** `false` Enable dragging for this element. Set `true` to drag in both directions. Set `"x"` or `"y"` to only drag in a specific direction. <motion.div drag /> #### `whileDrag` Target or variants to label to while the drag gesture is active. <!-- // As target --> <motion.div drag :whileDrag\="{ scale: 0.9 }" /> <!-- // As variants --> <motion.div drag whileDrag\="dragging" /> #### `dragConstraints` Applies constraints on the draggable area. Set as an object of optional `top`, `left`, `right`, and `bottom` values, measured in pixels: <motion.div drag\="x" :dragConstraints\="{ left: 0, right: 300 }" /> Or as a `ref` to another element to use its bounding box as the draggable constraints: <script setup\> import { useDomRef } from "motion-v" const constraintsRef = useDomRef() </script\> <template\> <motion.div ref\="constraintsRef"\> <motion.div drag :dragConstraints\="constraintsRef" /> </motion.div\> </template\> #### `dragSnapToOrigin` **Default:** `false` If `true`, the draggable element will animate back to its center/origin when released. <motion.div drag dragSnapToOrigin /> #### `dragElastic` **Default:** `0.5` The degree of movement allowed outside constraints. `0` = no movement, `1` = full movement. Set to `0.5` by default. Can also be set as `false` to disable movement. By passing an object of `top`/`right`/`bottom`/`left`, individual values can be set per constraint. Any missing values will be set to `0`. <motion.div drag :dragConstraints\="{ left: 0, right: 300 }" :dragElastic\="0.2" /> #### `dragMomentum` **Default:** `true` Apply momentum from the pan gesture to the component when dragging finishes. Set to `true` by default. <motion.div drag :dragConstraints\="{ left: 0, right: 300 }" :dragMomentum\="false" /> #### `dragTransition` Allows you to change dragging momentum transition. When releasing a draggable element, an animation with type `"inertia"` starts. The animation is based on your dragging velocity. This property allows you to customize it. <motion.div drag :dragTransition\="{ bounceStiffness: 600, bounceDamping: 10 }" /> #### `dragDirectionLock` **Default:** `false` Locks drag direction into the soonest detected direction. For example, if the component is moved more on the `x` axis than `y` axis before the drag gesture kicks in, it will **only** drag on the `x` axis for the remainder of the gesture. <motion.div drag dragDirectionLock /> #### `dragPropagation` **Default:** `false` Allows drag gesture propagation to child components. <motion.div drag\="x" dragPropagation /> #### `dragControls` Usually, dragging is initiated by pressing down on a component and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we might want to initiate dragging from a different component than the draggable one. By creating a `dragControls` using the `useDragControls` hook, we can pass this into the draggable component's `dragControls` prop. It exposes a `start` method that can start dragging from pointer events on other components. <script setup\> const dragControls = useDragControls() function startDrag(event) { dragControls.start(event, { snapToCursor: true }) } </script\> <template\> <div @pointerDown\="startDrag" /> <motion.div drag\="x" :dragControls\="dragControls" /> </template\> **Note:** Given that by setting `dragControls` you are taking control of initiating the drag gesture, it is possible to disable the draggable element as the initiator by setting `:dragListener="false"`. #### `dragListener` Determines whether to trigger the drag gesture from event listeners. If passing `dragControls`, setting this to `false` will ensure dragging can only be initiated by the controls, rather than a `pointerdown` event on the draggable element. #### `onDrag` Callback function that fires when the drag gesture is recognised on this element. function onDrag(event, info) { console.log(info.point.x, info.point.y) } <motion.div drag @drag\="onDrag" /> Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following: * `point`: Relative to the device or page. * `delta`: Distance since the last event. * `offset`: Distance from the original event. * `velocity`: Current velocity of the pointer. #### `onDragStart` Callback function that fires when a drag gesture starts. Provided the triggering `PointerEvent` and `info`. <motion.div drag @dragStart\="(event, info) => console.log(info.delta.x)" /> #### `onDragEnd` Callback function that fires when a drag gesture ends. Provided the triggering `PointerEvent` and `info`. <motion.div drag @dragEnd\="(event, info) => console.log(info.delta.x)" /> #### `onDirectionLock` Callback function that fires a drag direction is determined. <motion.div drag dragDirectionLock @directionLock\="axis => console.log(axis)" /> ### Viewport #### `whileInView` Target or variants to label to while the element is in view. <!-- As target --> <motion.div :whileInView\="{ opacity: 1 }" /> // As variants <motion.div whileInView\="visible" /> #### `inViewOptions` Options to define how the element is tracked within the viewport. <motion.section :whileInView\="{ opacity: 1 }" :inViewOptions\="{ once: true }" /> Available options: * `once`: If `true`, once element enters the viewport it won't detect subsequent leave/enter events. * `root`: The `ref` of an ancestor scrollable element to detect intersections with (instead of `window`). * `margin`: A margin to add to the viewport to change the detection area. Defaults to `"0px"`. Use multiple values to adjust top/right/bottom/left, e.g. `"0px -20px 0px 100px"`. * `amount`: The amount of an element that should enter the viewport to be considered "entered". Either `"some"`, `"all"` or a number between `0` and `1`. Defaults to `"some"`. #### `onViewportEnter` Callback function that fires when an element enters the viewport. Provided the `IntersectionObserverEntry` with details of the intersection event. <motion.div @viewportEnter\="(entry) \=> console.log(entry.isIntersecting)" /> #### `onViewportLeave` Callback function that fires when an element enters the viewport. Provided the `IntersectionObserverEntry` with details of the intersection event. <motion.div @viewportLeave\="(entry) \=> console.log(entry.intersectionRect)" /> ### Layout #### `layout` **Default:** `false` If `true`, this component will animate changes to its layout. <motion.div layout /> If set to `"position"` or `"size"`, only its position or size will animate, respectively. <motion.img layout\="position" /> #### `layoutId` If set, this component will animate changes to its layout. Additionally, when a new element enters the DOM and an element already exists with a matching `layoutId`, it will animate out from the previous element's size/position. <motion.li v-for\="item in items" layout\> {{item.name}} <motion.div v-if\="item.isSelected" layoutId\="underline" /> </motion.li\> If the previous component remains in the tree, the two elements will crossfade. #### `layoutDependency` By default, layout changes are detected every render. To reduce measurements and thus improve performance, you can pass a `layoutDependency` prop. Measurements will only occur when this value changes. <motion.nav layout :layoutDependency\="isOpen" /> #### `layoutScroll` For layout animations to work correctly within scrollable elements, their scroll offset needs measuring. For performance reasons, `Motion` doesn't measure the scroll offset of every ancestor. Add the `layoutScroll` prop to elements that should be measured. <motion.div layoutScroll style\="{ overflow: 'scroll' }"\> <motion.div layout /> </motion.div\> #### `layoutRoot` For layout animations to work correctly within `position: fixed` elements, we need to account for page scroll. Add `layoutRoot` to mark an element as `position: fixed`. <motion.div layoutRoot :style\="{ position: 'fixed' }"\> <motion.div layout /> </motion.div\> #### `onLayoutAnimationStart` A callback to run when a layout animation starts. #### `onLayoutAnimationComplete` A callback to run when a layout animation completes. ### Advanced #### `inherit` Set to `false` to prevent a component inheriting or propagating changes in a parent variant. #### `custom` Custom data to pass through to dynamic variants. <script setup\> const variants = { visible: (custom) \=> ({ opacity: 1, transition: { delay: custom \* 0.2 } }) } </script\> <template\> <motion.ul animate\="visible"\> <motion.li :custom\="0" :variants\="variants" /> <motion.li :custom\="1" :variants\="variants" /> <motion.li :custom\="2" :variants\="variants" /> </motion.ul\> </template\> #### `transformTemplate` By default, transforms are applied in order of `translate`, `scale`, `rotate` and `skew`. To change this, `transformTemplate` can be set as a function that accepts the latest transforms and the generated transform string and returns a new transform string. <!-- // Use the latest transform values --> <motion.div :style\="{ x: 0, rotate: 180 }" :transformTemplate\="({ x, rotate }) \=> \`rotate(${rotate}deg) translateX(${x}px)\`" /> <!-- // Or the generated transform string --> <motion.div :style\="{ x: 0, rotate: 180 }" :transformTemplate\="(latest, generated) \=> \`translate(-50%, -50%) ${generated}\`" /> The `motion` component drives most animations in Motion for Vue. There's a `motion` component for every HTML and SVG element, for instance `motion.div`, `motion.circle` etc. Think of it as a normal Vue component, supercharged for 120fps animation and gestures. ## Usage Import `motion` from `motion-v`: import { motion } from "motion-v" Now you can use it exactly as you would any normal HTML/SVG component: <motion.div class\="box" /> But you also gain access to powerful animation APIs like the `animate`, `layout`, `whileInView` props and much more. <motion.div class\="box" // Animate when this value changes: :animate\="{ scale: 2 }" // Fade in when the element enters the viewport: :whileInView\="{ opacity: 1 }" // Animate the component when its layout changes: layout // Style now supports indepedent transforms: :style\="{ x: 100 }" /> Check out the Animation guide for a full overview on animations in Motion for Vue. ### Performance `motion` components animate values outside the Vue render cycle for improved performance. Using motion values instead of Vue state to update `style` will also avoid re-renders. <script setup\> import { useMotionValue } from "motion-v" const x = useMotionValue(0) let timeout; onMounted(() \=> { // Won't trigger a re-render! timeout = setTimeout(() \=> x.set(100), 1000) }) onUnMounted(()\=>{ clearTimeout(timeout) }) </script\> <template\> <motion.div :style\="{ x }" /> </template\> ### Server-side rendering `motion` components are fully compatible with server-side rendering, meaning the initial state of the component will be reflected in the server-generated output. // Server will output \`translateX(100px)\` <motion.div :initial\="false" :animate\="{ x: 100 }" /> This is with the exception of some SVG attributes like `transform` which require DOM measurements to calculate. ### Custom components Any Vue component can be supercharged into a `motion` component by passing it to `motion.create()` as a function. const MotionComponent = motion.create(Component) **Important:** Make sure **not** to call `motion.create()` within template! This will make a new component every render, breaking your animations. It's also possible to pass strings to `motion.create`, which will create custom DOM elements. // Will render <custom-element /> into HTML const MotionComponent = motion.create('custom-element') By default, all `motion` props (like `animate` etc) are filtered out of the `props` forwarded to the provided component. By providing a `forwardMotionProps` config, the provided component will receive these props. motion.create(Component, { forwardMotionProps: true }) ## Props `motion` components accept the following props. ### Animation #### `initial` The initial visual state of the `motion` component. This can be set as an animation target: <motion.section :initial\="{ opacity: 0, x: 0 }" /> Variants: <motion.li initial\="visible" /> <motion.div :initial\="\['visible', 'active'\]" /> Or set as `false` to disable the enter animation and initially render as the values found in `animate`. <motion.div :initial\="false" :animate\="{ opacity: 0 }" /> #### `animate` A target to animate to on enter, and on update. Can be set as an animation target: <motion.div :initial\="{ boxShadow: '0px 0px #000' }" :animate\="{ boxShadow: '10px 10px #000' }" /> Or variants: <motion.li animate\="visible" /> <motion.div initial\="hidden" :animate\="\['visible', 'active'\]" /> #### `exit` A target to animate to when a component is removed from the tree. Can be set either as an animation target, or variant. **Note:** Owing to Vue Transition component limitations, the component being removed **must** be a **direct child** of `AnimatePresence` to enable this animation. <AnimatePresence\> <ul v-if\="isVisible" key\="list"\> <motion.li :exit\="{ opacity: 0 }" /> </ul\> </AnimatePresence\> #### `transition` The default transition for this component to use when an animation prop (`animate`, `whileHover` etc) has no `transition` defined. <motion.div :transition\="{ type: 'spring' }" :animate\="{ scale: 1.2 }" /> #### `variants` The variants for this component. <script setup\> const variants = { active: { backgroundColor: "#f00" }, inactive: { backgroundColor: "#fff", transition: { duration: 2 } } } </script\> <template\> <motion.div :variants\="variants" :animate\="isActive ? 'active' : 'inactive'" /> </template\> #### `style` The normal Vue DOM `style` prop, with added support for motion values and independent transforms. <script setup\> const x = useMotionValue(30) </script\> <tempalte\> <motion.div :style\="{ x, rotate: 90, originX: 0.5 }" /> </tempalte\> #### `onUpdate` Callback triggered every frame any value on the `motion` component updates. It's provided a single argument with the latest values. <motion.article :animate\="{ opacity: 1 }" :@update\="latest \=> console.log(latest.opacity)" /> #### `onAnimationStart` Callback triggered when any animation (except layout animations, see `onLayoutAnimationStart`) starts. It's provided a single argument, with the target or variant name of the started animation. <motion.circle :animate\="{ r: 10 }" @animationStart\="latest \=> console.log(latest.r)" /> #### `onAnimationComplete` Callback triggered when any animation (except layout animations, see `onLayoutAnimationComplete`) completes. It's provided a single argument, with the target or variant name of the completed animation. <motion.circle :animate\="{ r: 10 }" @animationComplete\="latest \=> console.log(latest.r)" /> ### Hover #### `whileHover` Target or variants to label to while the hover gesture is active. <!-- As target --> <motion.button :whileHover\="{ scale: 1.2 }" /> <!-- As variants --> <motion.div whileHover\="hovered" /> #### `onHoverStart` Callback function that fires when a pointer starts hovering over the component. Provided the triggering `PointerEvent`. <motion.div @hoverStart\="(event) \=> console.log(event)" /> #### `onHoverEnd` Callback function that fires when a pointer stops hovering over the component. Provided the triggering `PointerEvent`. <motion.div @hoverEnd\="(event) \=> console.log(event)" /> ### Press #### `whilePress` Target or variants to label to while the press gesture is active. <!-- // As target --> <motion.button :whilePress\="{ scale: 0.9 }" /> <!-- // As variants --> <motion.div whilePress\="tapped" /> #### `onPressStart` Callback function that fires when a pointer starts pressing the component. Provided the triggering `PointerEvent`. <motion.div @pressStart\="(event) \=> console.log(event)" /> #### `onPress` Callback function that fires when a pointer stops pressing the component and the pointer was released **inside** the component. Provided the triggering `PointerEvent`. <motion.div @press\="(event) \=> console.log(event)" /> #### `onPressCancel` Callback function that fires when a pointer stops pressing the component and the pointer was released **outside** the component. Provided the triggering `PointerEvent`. <motion.div @pressCancel\="(event) \=> console.log(event)" /> ### Focus #### `whileFocus` Target or variants to label to while the focus gesture is active. <!-- As target --> <motion.button :whileFocus\="{ outline: 'dashed #000' }" /> <!-- As variants --> <motion.div whileFocus\="focused" /> ### Pan #### `onPan` Callback function that fires when the pan gesture is recognised on this element. **Note:** For pan gestures to work correctly with touch input, the element needs touch scrolling to be disabled on either x/y or both axis with the `touch-action` CSS rule. function onPan(event, info) { console.log(info.point.x, info.point.y) } <motion.div @pan\="onPan" /> Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following: * `point`: Relative to the device or page. * `delta`: Distance since the last event. * `offset`: Distance from the original event. * `velocity`: Current velocity of the pointer. #### `onPanStart` Callback function that fires when a pan gesture starts. Provided the triggering `PointerEvent` and `info`. <motion.div @panStart\="(event, info) \=> console.log(info.delta.x)" /> #### `onPanEnd` Callback function that fires when a pan gesture ends. Provided the triggering `PointerEvent` and `info`. <motion.div @panEnd\="(event, info) \=> console.log(info.delta.x)" /> #### Drag #### `drag` **Default:** `false` Enable dragging for this element. Set `true` to drag in both directions. Set `"x"` or `"y"` to only drag in a specific direction. <motion.div drag /> #### `whileDrag` Target or variants to label to while the drag gesture is active. <!-- // As target --> <motion.div drag :whileDrag\="{ scale: 0.9 }" /> <!-- // As variants --> <motion.div drag whileDrag\="dragging" /> #### `dragConstraints` Applies constraints on the draggable area. Set as an object of optional `top`, `left`, `right`, and `bottom` values, measured in pixels: <motion.div drag\="x" :dragConstraints\="{ left: 0, right: 300 }" /> Or as a `ref` to another element to use its bounding box as the draggable constraints: <script setup\> import { useDomRef } from "motion-v" const constraintsRef = useDomRef() </script\> <template\> <motion.div ref\="constraintsRef"\> <motion.div drag :dragConstraints\="constraintsRef" /> </motion.div\> </template\> #### `dragSnapToOrigin` **Default:** `false` If `true`, the draggable element will animate back to its center/origin when released. <motion.div drag dragSnapToOrigin /> #### `dragElastic` **Default:** `0.5` The degree of movement allowed outside constraints. `0` = no movement, `1` = full movement. Set to `0.5` by default. Can also be set as `false` to disable movement. By passing an object of `top`/`right`/`bottom`/`left`, individual values can be set per constraint. Any missing values will be set to `0`. <motion.div drag :dragConstraints\="{ left: 0, right: 300 }" :dragElastic\="0.2" /> #### `dragMomentum` **Default:** `true` Apply momentum from the pan gesture to the component when dragging finishes. Set to `true` by default. <motion.div drag :dragConstraints\="{ left: 0, right: 300 }" :dragMomentum\="false" /> #### `dragTransition` Allows you to change dragging momentum transition. When releasing a draggable element, an animation with type `"inertia"` starts. The animation is based on your dragging velocity. This property allows you to customize it. <motion.div drag :dragTransition\="{ bounceStiffness: 600, bounceDamping: 10 }" /> #### `dragDirectionLock` **Default:** `false` Locks drag direction into the soonest detected direction. For example, if the component is moved more on the `x` axis than `y` axis before the drag gesture kicks in, it will **only** drag on the `x` axis for the remainder of the gesture. <motion.div drag dragDirectionLock /> #### `dragPropagation` **Default:** `false` Allows drag gesture propagation to child components. <motion.div drag\="x" dragPropagation /> #### `dragControls` Usually, dragging is initiated by pressing down on a component and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we might want to initiate dragging from a different component than the draggable one. By creating a `dragControls` using the `useDragControls` hook, we can pass this into the draggable component's `dragControls` prop. It exposes a `start` method that can start dragging from pointer events on other components. <script setup\> const dragControls = useDragControls() function startDrag(event) { dragControls.start(event, { snapToCursor: true }) } </script\> <template\> <div @pointerDown\="startDrag" /> <motion.div drag\="x" :dragControls\="dragControls" /> </template\> **Note:** Given that by setting `dragControls` you are taking control of initiating the drag gesture, it is possible to disable the draggable element as the initiator by setting `:dragListener="false"`. #### `dragListener` Determines whether to trigger the drag gesture from event listeners. If passing `dragControls`, setting this to `false` will ensure dragging can only be initiated by the controls, rather than a `pointerdown` event on the draggable element. #### `onDrag` Callback function that fires when the drag gesture is recognised on this element. function onDrag(event, info) { console.log(info.point.x, info.point.y) } <motion.div drag @drag\="onDrag" /> Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following: * `point`: Relative to the device or page. * `delta`: Distance since the last event. * `offset`: Distance from the original event. * `velocity`: Current velocity of the pointer. #### `onDragStart` Callback function that fires when a drag gesture starts. Provided the triggering `PointerEvent` and `info`. <motion.div drag @dragStart\="(event, info) => console.log(info.delta.x)" /> #### `onDragEnd` Callback function that fires when a drag gesture ends. Provided the triggering `PointerEvent` and `info`. <motion.div drag @dragEnd\="(event, info) => console.log(info.delta.x)" /> #### `onDirectionLock` Callback function that fires a drag direction is determined. <motion.div drag dragDirectionLock @directionLock\="axis => console.log(axis)" /> ### Viewport #### `whileInView` Target or variants to label to while the element is in view. <!-- As target --> <motion.div :whileInView\="{ opacity: 1 }" /> // As variants <motion.div whileInView\="visible" /> #### `inViewOptions` Options to define how the element is tracked within the viewport. <motion.section :whileInView\="{ opacity: 1 }" :inViewOptions\="{ once: true }" /> Available options: * `once`: If `true`, once element enters the viewport it won't detect subsequent leave/enter events. * `root`: The `ref` of an ancestor scrollable element to detect intersections with (instead of `window`). * `margin`: A margin to add to the viewport to change the detection area. Defaults to `"0px"`. Use multiple values to adjust top/right/bottom/left, e.g. `"0px -20px 0px 100px"`. * `amount`: The amount of an element that should enter the viewport to be considered "entered". Either `"some"`, `"all"` or a number between `0` and `1`. Defaults to `"some"`. #### `onViewportEnter` Callback function that fires when an element enters the viewport. Provided the `IntersectionObserverEntry` with details of the intersection event. <motion.div @viewportEnter\="(entry) \=> console.log(entry.isIntersecting)" /> #### `onViewportLeave` Callback function that fires when an element enters the viewport. Provided the `IntersectionObserverEntry` with details of the intersection event. <motion.div @viewportLeave\="(entry) \=> console.log(entry.intersectionRect)" /> ### Layout #### `layout` **Default:** `false` If `true`, this component will animate changes to its layout. <motion.div layout /> If set to `"position"` or `"size"`, only its position or size will animate, respectively. <motion.img layout\="position" /> #### `layoutId` If set, this component will animate changes to its layout. Additionally, when a new element enters the DOM and an element already exists with a matching `layoutId`, it will animate out from the previous element's size/position. <motion.li v-for\="item in items" layout\> {{item.name}} <motion.div v-if\="item.isSelected" layoutId\="underline" /> </motion.li\> If the previous component remains in the tree, the two elements will crossfade. #### `layoutDependency` By default, layout changes are detected every render. To reduce measurements and thus improve performance, you can pass a `layoutDependency` prop. Measurements will only occur when this value changes. <motion.nav layout :layoutDependency\="isOpen" /> #### `layoutScroll` For layout animations to work correctly within scrollable elements, their scroll offset needs measuring. For performance reasons, `Motion` doesn't measure the scroll offset of every ancestor. Add the `layoutScroll` prop to elements that should be measured. <motion.div layoutScroll style\="{ overflow: 'scroll' }"\> <motion.div layout /> </motion.div\> #### `layoutRoot` For layout animations to work correctly within `position: fixed` elements, we need to account for page scroll. Add `layoutRoot` to mark an element as `position: fixed`. <motion.div layoutRoot :style\="{ position: 'fixed' }"\> <motion.div layout /> </motion.div\> #### `onLayoutAnimationStart` A callback to run when a layout animation starts. #### `onLayoutAnimationComplete` A callback to run when a layout animation completes. ### Advanced #### `inherit` Set to `false` to prevent a component inheriting or propagating changes in a parent variant. #### `custom` Custom data to pass through to dynamic variants. <script setup\> const variants = { visible: (custom) \=> ({ opacity: 1, transition: { delay: custom \* 0.2 } }) } </script\> <template\> <motion.ul animate\="visible"\> <motion.li :custom\="0" :variants\="variants" /> <motion.li :custom\="1" :variants\="variants" /> <motion.li :custom\="2" :variants\="variants" /> </motion.ul\> </template\> #### `transformTemplate` By default, transforms are applied in order of `translate`, `scale`, `rotate` and `skew`. To change this, `transformTemplate` can be set as a function that accepts the latest transforms and the generated transform string and returns a new transform string. <!-- // Use the latest transform values --> <motion.div :style\="{ x: 0, rotate: 180 }" :transformTemplate\="({ x, rotate }) \=> \`rotate(${rotate}deg) translateX(${x}px)\`" /> <!-- // Or the generated transform string --> <motion.div :style\="{ x: 0, rotate: 180 }" :transformTemplate\="(latest, generated) \=> \`translate(-50%, -50%) ${generated}\`" /> The `motion` component drives most animations in Motion for Vue. There's a `motion` component for every HTML and SVG element, for instance `motion.div`, `motion.circle` etc. Think of it as a normal Vue component, supercharged for 120fps animation and gestures. ## Usage Import `motion` from `motion-v`: import { motion } from "motion-v" Now you can use it exactly as you would any normal HTML/SVG component: <motion.div class\="box" /> But you also gain access to powerful animation APIs like the `animate`, `layout`, `whileInView` props and much more. <motion.div class\="box" // Animate when this value changes: :animate\="{ scale: 2 }" // Fade in when the element enters the viewport: :whileInView\="{ opacity: 1 }" // Animate the component when its layout changes: layout // Style now supports indepedent transforms: :style\="{ x: 100 }" /> Check out the Animation guide for a full overview on animations in Motion for Vue. ### Performance `motion` components animate values outside the Vue render cycle for improved performance. Using motion values instead of Vue state to update `style` will also avoid re-renders. <script setup\> import { useMotionValue } from "motion-v" const x = useMotionValue(0) let timeout; onMounted(() \=> { // Won't trigger a re-render! timeout = setTimeout(() \=> x.set(100), 1000) }) onUnMounted(()\=>{ clearTimeout(timeout) }) </script\> <template\> <motion.div :style\="{ x }" /> </template\> ### Server-side rendering `motion` components are fully compatible with server-side rendering, meaning the initial state of the component will be reflected in the server-generated output. // Server will output \`translateX(100px)\` <motion.div :initial\="false" :animate\="{ x: 100 }" /> This is with the exception of some SVG attributes like `transform` which require DOM measurements to calculate. ### Custom components Any Vue component can be supercharged into a `motion` component by passing it to `motion.create()` as a function. const MotionComponent = motion.create(Component) **Important:** Make sure **not** to call `motion.create()` within template! This will make a new component every render, breaking your animations. It's also possible to pass strings to `motion.create`, which will create custom DOM elements. // Will render <custom-element /> into HTML const MotionComponent = motion.create('custom-element') By default, all `motion` props (like `animate` etc) are filtered out of the `props` forwarded to the provided component. By providing a `forwardMotionProps` config, the provided component will receive these props. motion.create(Component, { forwardMotionProps: true }) ## Props `motion` components accept the following props. ### Animation #### `initial` The initial visual state of the `motion` component. This can be set as an animation target: <motion.section :initial\="{ opacity: 0, x: 0 }" /> Variants: <motion.li initial\="visible" /> <motion.div :initial\="\['visible', 'active'\]" /> Or set as `false` to disable the enter animation and initially render as the values found in `animate`. <motion.div :initial\="false" :animate\="{ opacity: 0 }" /> #### `animate` A target to animate to on enter, and on update. Can be set as an animation target: <motion.div :initial\="{ boxShadow: '0px 0px #000' }" :animate\="{ boxShadow: '10px 10px #000' }" /> Or variants: <motion.li animate\="visible" /> <motion.div initial\="hidden" :animate\="\['visible', 'active'\]" /> #### `exit` A target to animate to when a component is removed from the tree. Can be set either as an animation target, or variant. **Note:** Owing to Vue Transition component limitations, the component being removed **must** be a **direct child** of `AnimatePresence` to enable this animation. <AnimatePresence\> <ul v-if\="isVisible" key\="list"\> <motion.li :exit\="{ opacity: 0 }" /> </ul\> </AnimatePresence\> #### `transition` The default transition for this component to use when an animation prop (`animate`, `whileHover` etc) has no `transition` defined. <motion.div :transition\="{ type: 'spring' }" :animate\="{ scale: 1.2 }" /> #### `variants` The variants for this component. <script setup\> const variants = { active: { backgroundColor: "#f00" }, inactive: { backgroundColor: "#fff", transition: { duration: 2 } } } </script\> <template\> <motion.div :variants\="variants" :animate\="isActive ? 'active' : 'inactive'" /> </template\> #### `style` The normal Vue DOM `style` prop, with added support for motion values and independent transforms. <script setup\> const x = useMotionValue(30) </script\> <tempalte\> <motion.div :style\="{ x, rotate: 90, originX: 0.5 }" /> </tempalte\> #### `onUpdate` Callback triggered every frame any value on the `motion` component updates. It's provided a single argument with the latest values. <motion.article :animate\="{ opacity: 1 }" :@update\="latest \=> console.log(latest.opacity)" /> #### `onAnimationStart` Callback triggered when any animation (except layout animations, see `onLayoutAnimationStart`) starts. It's provided a single argument, with the target or variant name of the started animation. <motion.circle :animate\="{ r: 10 }" @animationStart\="latest \=> console.log(latest.r)" /> #### `onAnimationComplete` Callback triggered when any animation (except layout animations, see `onLayoutAnimationComplete`) completes. It's provided a single argument, with the target or variant name of the completed animation. <motion.circle :animate\="{ r: 10 }" @animationComplete\="latest \=> console.log(latest.r)" /> ### Hover #### `whileHover` Target or variants to label to while the hover gesture is active. <!-- As target --> <motion.button :whileHover\="{ scale: 1.2 }" /> <!-- As variants --> <motion.div whileHover\="hovered" /> #### `onHoverStart` Callback function that fires when a pointer starts hovering over the component. Provided the triggering `PointerEvent`. <motion.div @hoverStart\="(event) \=> console.log(event)" /> #### `onHoverEnd` Callback function that fires when a pointer stops hovering over the component. Provided the triggering `PointerEvent`. <motion.div @hoverEnd\="(event) \=> console.log(event)" /> ### Press #### `whilePress` Target or variants to label to while the press gesture is active. <!-- // As target --> <motion.button :whilePress\="{ scale: 0.9 }" /> <!-- // As variants --> <motion.div whilePress\="tapped" /> #### `onPressStart` Callback function that fires when a pointer starts pressing the component. Provided the triggering `PointerEvent`. <motion.div @pressStart\="(event) \=> console.log(event)" /> #### `onPress` Callback function that fires when a pointer stops pressing the component and the pointer was released **inside** the component. Provided the triggering `PointerEvent`. <motion.div @press\="(event) \=> console.log(event)" /> #### `onPressCancel` Callback function that fires when a pointer stops pressing the component and the pointer was released **outside** the component. Provided the triggering `PointerEvent`. <motion.div @pressCancel\="(event) \=> console.log(event)" /> ### Focus #### `whileFocus` Target or variants to label to while the focus gesture is active. <!-- As target --> <motion.button :whileFocus\="{ outline: 'dashed #000' }" /> <!-- As variants --> <motion.div whileFocus\="focused" /> ### Pan #### `onPan` Callback function that fires when the pan gesture is recognised on this element. **Note:** For pan gestures to work correctly with touch input, the element needs touch scrolling to be disabled on either x/y or both axis with the `touch-action` CSS rule. function onPan(event, info) { console.log(info.point.x, info.point.y) } <motion.div @pan\="onPan" /> Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following: * `point`: Relative to the device or page. * `delta`: Distance since the last event. * `offset`: Distance from the original event. * `velocity`: Current velocity of the pointer. #### `onPanStart` Callback function that fires when a pan gesture starts. Provided the triggering `PointerEvent` and `info`. <motion.div @panStart\="(event, info) \=> console.log(info.delta.x)" /> #### `onPanEnd` Callback function that fires when a pan gesture ends. Provided the triggering `PointerEvent` and `info`. <motion.div @panEnd\="(event, info) \=> console.log(info.delta.x)" /> #### Drag #### `drag` **Default:** `false` Enable dragging for this element. Set `true` to drag in both directions. Set `"x"` or `"y"` to only drag in a specific direction. <motion.div drag /> #### `whileDrag` Target or variants to label to while the drag gesture is active. <!-- // As target --> <motion.div drag :whileDrag\="{ scale: 0.9 }" /> <!-- // As variants --> <motion.div drag whileDrag\="dragging" /> #### `dragConstraints` Applies constraints on the draggable area. Set as an object of optional `top`, `left`, `right`, and `bottom` values, measured in pixels: <motion.div drag\="x" :dragConstraints\="{ left: 0, right: 300 }" /> Or as a `ref` to another element to use its bounding box as the draggable constraints: <script setup\> import { useDomRef } from "motion-v" const constraintsRef = useDomRef() </script\> <template\> <motion.div ref\="constraintsRef"\> <motion.div drag :dragConstraints\="constraintsRef" /> </motion.div\> </template\> #### `dragSnapToOrigin` **Default:** `false` If `true`, the draggable element will animate back to its center/origin when released. <motion.div drag dragSnapToOrigin /> #### `dragElastic` **Default:** `0.5` The degree of movement allowed outside constraints. `0` = no movement, `1` = full movement. Set to `0.5` by default. Can also be set as `false` to disable movement. By passing an object of `top`/`right`/`bottom`/`left`, individual values can be set per constraint. Any missing values will be set to `0`. <motion.div drag :dragConstraints\="{ left: 0, right: 300 }" :dragElastic\="0.2" /> #### `dragMomentum` **Default:** `true` Apply momentum from the pan gesture to the component when dragging finishes. Set to `true` by default. <motion.div drag :dragConstraints\="{ left: 0, right: 300 }" :dragMomentum\="false" /> #### `dragTransition` Allows you to change dragging momentum transition. When releasing a draggable element, an animation with type `"inertia"` starts. The animation is based on your dragging velocity. This property allows you to customize it. <motion.div drag :dragTransition\="{ bounceStiffness: 600, bounceDamping: 10 }" /> #### `dragDirectionLock` **Default:** `false` Locks drag direction into the soonest detected direction. For example, if the component is moved more on the `x` axis than `y` axis before the drag gesture kicks in, it will **only** drag on the `x` axis for the remainder of the gesture. <motion.div drag dragDirectionLock /> #### `dragPropagation` **Default:** `false` Allows drag gesture propagation to child components. <motion.div drag\="x" dragPropagation /> #### `dragControls` Usually, dragging is initiated by pressing down on a component and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we might want to initiate dragging from a different component than the draggable one. By creating a `dragControls` using the `useDragControls` hook, we can pass this into the draggable component's `dragControls` prop. It exposes a `start` method that can start dragging from pointer events on other components. <script setup\> const dragControls = useDragControls() function startDrag(event) { dragControls.start(event, { snapToCursor: true }) } </script\> <template\> <div @pointerDown\="startDrag" /> <motion.div drag\="x" :dragControls\="dragControls" /> </template\> **Note:** Given that by setting `dragControls` you are taking control of initiating the drag gesture, it is possible to disable the draggable element as the initiator by setting `:dragListener="false"`. #### `dragListener` Determines whether to trigger the drag gesture from event listeners. If passing `dragControls`, setting this to `false` will ensure dragging can only be initiated by the controls, rather than a `pointerdown` event on the draggable element. #### `onDrag` Callback function that fires when the drag gesture is recognised on this element. function onDrag(event, info) { console.log(info.point.x, info.point.y) } <motion.div drag @drag\="onDrag" /> Pan and drag events are provided the origin `PointerEvent` as well as an object `info` that contains `x` and `y` point values for the following: * `point`: Relative to the device or page. * `delta`: Distance since the last event. * `offset`: Distance from the original event. * `velocity`: Current velocity of the pointer. #### `onDragStart` Callback function that fires when a drag gesture starts. Provided the triggering `PointerEvent` and `info`. <motion.div drag @dragStart\="(event, info) => console.log(info.delta.x)" /> #### `onDragEnd` Callback function that fires when a drag gesture ends. Provided the triggering `PointerEvent` and `info`. <motion.div drag @dragEnd\="(event, info) => console.log(info.delta.x)" /> #### `onDirectionLock` Callback function that fires a drag direction is determined. <motion.div drag dragDirectionLock @directionLock\="axis => console.log(axis)" /> ### Viewport #### `whileInView` Target or variants to label to while the element is in view. <!-- As target --> <motion.div :whileInView\="{ opacity: 1 }" /> // As variants <motion.div whileInView\="visible" /> #### `inViewOptions` Options to define how the element is tracked within the viewport. <motion.section :whileInView\="{ opacity: 1 }" :inViewOptions\="{ once: true }" /> Available options: * `once`: If `true`, once element enters the viewport it won't detect subsequent leave/enter events. * `root`: The `ref` of an ancestor scrollable element to detect intersections with (instead of `window`). * `margin`: A margin to add to the viewport to change the detection area. Defaults to `"0px"`. Use multiple values to adjust top/right/bottom/left, e.g. `"0px -20px 0px 100px"`. * `amount`: The amount of an element that should enter the viewport to be considered "entered". Either `"some"`, `"all"` or a number between `0` and `1`. Defaults to `"some"`. #### `onViewportEnter` Callback function that fires when an element enters the viewport. Provided the `IntersectionObserverEntry` with details of the intersection event. <motion.div @viewportEnter\="(entry) \=> console.log(entry.isIntersecting)" /> #### `onViewportLeave` Callback function that fires when an element enters the viewport. Provided the `IntersectionObserverEntry` with details of the intersection event. <motion.div @viewportLeave\="(entry) \=> console.log(entry.intersectionRect)" /> ### Layout #### `layout` **Default:** `false` If `true`, this component will animate changes to its layout. <motion.div layout /> If set to `"position"` or `"size"`, only its position or size will animate, respectively. <motion.img layout\="position" /> #### `layoutId` If set, this component will animate changes to its layout. Additionally, when a new element enters the DOM and an element already exists with a matching `layoutId`, it will animate out from the previous element's size/position. <motion.li v-for\="item in items" layout\> {{item.name}} <motion.div v-if\="item.isSelected" layoutId\="underline" /> </motion.li\> If the previous component remains in the tree, the two elements will crossfade. #### `layoutDependency` By default, layout changes are detected every render. To reduce measurements and thus improve performance, you can pass a `layoutDependency` prop. Measurements will only occur when this value changes. <motion.nav layout :layoutDependency\="isOpen" /> #### `layoutScroll` For layout animations to work correctly within scrollable elements, their scroll offset needs measuring. For performance reasons, `Motion` doesn't measure the scroll offset of every ancestor. Add the `layoutScroll` prop to elements that should be measured. <motion.div layoutScroll style\="{ overflow: 'scroll' }"\> <motion.div layout /> </motion.div\> #### `layoutRoot` For layout animations to work correctly within `position: fixed` elements, we need to account for page scroll. Add `layoutRoot` to mark an element as `position: fixed`. <motion.div layoutRoot :style\="{ position: 'fixed' }"\> <motion.div layout /> </motion.div\> #### `onLayoutAnimationStart` A callback to run when a layout animation starts. #### `onLayoutAnimationComplete` A callback to run when a layout animation completes. ### Advanced #### `inherit` Set to `false` to prevent a component inheriting or propagating changes in a parent variant. #### `custom` Custom data to pass through to dynamic variants. <script setup\> const variants = { visible: (custom) \=> ({ opacity: 1, transition: { delay: custom \* 0.2 } }) } </script\> <template\> <motion.ul animate\="visible"\> <motion.li :custom\="0" :variants\="variants" /> <motion.li :custom\="1" :variants\="variants" /> <motion.li :custom\="2" :variants\="variants" /> </motion.ul\> </template\> #### `transformTemplate` By default, transforms are applied in order of `translate`, `scale`, `rotate` and `skew`. To change this, `transformTemplate` can be set as a function that accepts the latest transforms and the generated transform string and returns a new transform string. <!-- // Use the latest transform values --> <motion.div :style\="{ x: 0, rotate: 180 }" :transformTemplate\="({ x, rotate }) \=> \`rotate(${rotate}deg) translateX(${x}px)\`" /> <!-- // Or the generated transform string --> <motion.div :style\="{ x: 0, rotate: 180 }" :transformTemplate\="(latest, generated) \=> \`translate(-50%, -50%) ${generated}\`" /> --- ## Page: https://motion.dev/docs/vue-animate-presence # AnimatePresence `AnimatePresence` makes exit animations easy. By wrapping one or more `motion` components with `AnimatePresence`, we gain access to the `exit` animation prop. <AnimatePresence\> <motion.div v-if\="show" key\="modal" :exit\="{ opacity: 0 }" />} </AnimatePresence\> ## Usage ### Import import { AnimatePresence } from "motion-v" ### Exit animations `AnimatePresence` works by detecting when its **direct children** are removed from the Vue tree. This can be due to a component mounting/remounting: <AnimatePresence\> <Modal v-if\="show" key\="modal" /> </AnimatePresence\> or using `v-show`: <AnimatePresence\> <Modal v-show\="show" key\="modal" /> </AnimatePresence\> Or Its `key` changing: <AnimatePresence\> <Slide :key\="activeItem.id" /> </AnimatePresence\> Or when children in a list are added/removed: <AnimatePresence\> <motion.li v-for\="item in items" :key\="item.id" :exit\="{ opacity: 1 }" layout /> </AnimatePresence\> Any `motion` components within the exiting component will fire animations defined on their `exit` props before the component is removed from the DOM. <template\> <motion.div :exit\="{ opacity: 0 }"\> <img :src\="img.src" /> <motion.p :exit\="{ y: 10 }"\>{{description}}</motion.p\> </motion.div\> </template\> **Note:** Direct children must each have a unique `key` prop so `AnimatePresence` can track their presence in the tree. Like `initial` and `animate`, `exit` can be defined either as an object of values, or as a variant label. <script setup\> const modalVariants = { visible: { opacity: 1, transition: { when: "beforeChildren" } }, hidden: { opacity: 0, transition: { when: "afterChildren" } } } </script\> <template\> <motion.div :variants\="modalVariants" initial\="hidden" animate\="visible" exit\="hidden" \> {{children}} </motion.div\> </template\> ### Changing `key` Changing a `key` prop makes Vue create an entirely new component. So by changing the `key` of a single child of `AnimatePresence`, we can easily make components like slideshows. <template\> <AnimatePresence\> <motion.img :key\="image.src" :src\="image.src" :initial\="{ x: 300, opacity: 0 }" :animate\="{ x: 0, opacity: 1 }" :exit\="{ x: -300, opacity: 0 }" /> </AnimatePresence\> </template\> ## Props ### `initial` By passing `:initial="false"`, `AnimatePresence` will disable any initial animations on children that are present when the component is first rendered. <AnimatePresence :initial\="false"\> <Slide :key\="activeItem.id" /> </AnimatePresence\> ### `custom` When a component is removed, there's no longer a chance to update its props (because it's no longer in the Vue tree). Therefore we can't update its exit animation with the same render that removed the component. By passing a value through `AnimatePresence`'s `custom` prop, we can use dynamic variants to change the `exit` animation. <script setup\> const variants = { hidden: (direction) \=> ({ opacity: 0, x: direction === 1 ? -300 : 300 }), visible: { opacity: 1, x: 0 } } </script\> <template\> <AnimatePresence :custom\="direction"\> <motion.img :key\="image.src" :src\="image.src" :variants\="variants" initial\="hidden" animate\="visible" exit\="hidden" /> </AnimatePresence\> </template\> ### `mode` **Default:** `"sync"` Decides how `AnimatePresence` handles entering and exiting children. * `"sync"`: Children animate in/out as soon as they're added/removed. * `"wait"`: The entering child will wait until the exiting child has animated out. **Note:** Currently only renders a single child at a time. * `"popLayout"`: Exiting children will be "popped" out of the page layout. This allows surrounding elements to move to their new layout immediately. ### `onExitComplete` Fires when all exiting nodes have completed animating out. ## Troubleshooting ### Exit animations aren't working Ensure all **immediate** children get a unique `key` prop that **remains the same for that component every render**. For instance, providing `index` as a `key` is **bad** because if the items reorder then the `index` will not be matched to the `item`: <AnimatePresence\> <Component v-for\="(item,index) in items" :key\="index" /> </AnimatePresence\> It's preferred to pass something that's unique to that item, for instance an ID: <AnimatePresence\> <Componen v-for\="(item,index) in items" :key\="item.id" /> </AnimatePresence\> Also make sure `AnimatePresence` is **outside** of the code that unmounts the element. If `AnimatePresence` itself unmounts, then it can't control exit animations! For example, this will **not work**: <AnimatePresence v-if\="isVisible"\> <Component /> </AnimatePresence\> Instead, the conditional should be at the root of `AnimatePresence`: <AnimatePresence\> <Component v-if\="isVisible" /> </AnimatePresence\> ### Layout animations not working with `mode="sync"` When mixing layout and exit animations, it might be necessary to wrap the group in `LayoutGroup` to ensure that components outside of `AnimatePresence` know when to perform a layout animation. <LayoutGroup\> <motion.ul layout\> <AnimatePresence\> <motion.li v-for\="item in items" layout :key\="item.id" /> </AnimatePresence\> </motion.ul\> </LayoutGroup\> ### Layout animations not working with `mode="popLayout"` When any HTML element has an active `transform` it temporarily becomes the offset parent of its children. This can cause children with `position: "absolute"` not to appear where you expect. `mode="popLayout"` works by using `position: "absolute"`. So to ensure consistent and expected positioning during a layout animation, ensure that the animating parent has a `position` other than `"static"`. <motion.ul layout :style\="{ position: 'relative' }"\> <AnimatePresence mode\="popLayout"\> <motion.li v-for\="item in items" layout :key\="item.id" /> </AnimatePresence\> </motion.ul\> `AnimatePresence` makes exit animations easy. By wrapping one or more `motion` components with `AnimatePresence`, we gain access to the `exit` animation prop. <AnimatePresence\> <motion.div v-if\="show" key\="modal" :exit\="{ opacity: 0 }" />} </AnimatePresence\> ## Usage ### Import import { AnimatePresence } from "motion-v" ### Exit animations `AnimatePresence` works by detecting when its **direct children** are removed from the Vue tree. This can be due to a component mounting/remounting: <AnimatePresence\> <Modal v-if\="show" key\="modal" /> </AnimatePresence\> or using `v-show`: <AnimatePresence\> <Modal v-show\="show" key\="modal" /> </AnimatePresence\> Or Its `key` changing: <AnimatePresence\> <Slide :key\="activeItem.id" /> </AnimatePresence\> Or when children in a list are added/removed: <AnimatePresence\> <motion.li v-for\="item in items" :key\="item.id" :exit\="{ opacity: 1 }" layout /> </AnimatePresence\> Any `motion` components within the exiting component will fire animations defined on their `exit` props before the component is removed from the DOM. <template\> <motion.div :exit\="{ opacity: 0 }"\> <img :src\="img.src" /> <motion.p :exit\="{ y: 10 }"\>{{description}}</motion.p\> </motion.div\> </template\> **Note:** Direct children must each have a unique `key` prop so `AnimatePresence` can track their presence in the tree. Like `initial` and `animate`, `exit` can be defined either as an object of values, or as a variant label. <script setup\> const modalVariants = { visible: { opacity: 1, transition: { when: "beforeChildren" } }, hidden: { opacity: 0, transition: { when: "afterChildren" } } } </script\> <template\> <motion.div :variants\="modalVariants" initial\="hidden" animate\="visible" exit\="hidden" \> {{children}} </motion.div\> </template\> ### Changing `key` Changing a `key` prop makes Vue create an entirely new component. So by changing the `key` of a single child of `AnimatePresence`, we can easily make components like slideshows. <template\> <AnimatePresence\> <motion.img :key\="image.src" :src\="image.src" :initial\="{ x: 300, opacity: 0 }" :animate\="{ x: 0, opacity: 1 }" :exit\="{ x: -300, opacity: 0 }" /> </AnimatePresence\> </template\> ## Props ### `initial` By passing `:initial="false"`, `AnimatePresence` will disable any initial animations on children that are present when the component is first rendered. <AnimatePresence :initial\="false"\> <Slide :key\="activeItem.id" /> </AnimatePresence\> ### `custom` When a component is removed, there's no longer a chance to update its props (because it's no longer in the Vue tree). Therefore we can't update its exit animation with the same render that removed the component. By passing a value through `AnimatePresence`'s `custom` prop, we can use dynamic variants to change the `exit` animation. <script setup\> const variants = { hidden: (direction) \=> ({ opacity: 0, x: direction === 1 ? -300 : 300 }), visible: { opacity: 1, x: 0 } } </script\> <template\> <AnimatePresence :custom\="direction"\> <motion.img :key\="image.src" :src\="image.src" :variants\="variants" initial\="hidden" animate\="visible" exit\="hidden" /> </AnimatePresence\> </template\> ### `mode` **Default:** `"sync"` Decides how `AnimatePresence` handles entering and exiting children. * `"sync"`: Children animate in/out as soon as they're added/removed. * `"wait"`: The entering child will wait until the exiting child has animated out. **Note:** Currently only renders a single child at a time. * `"popLayout"`: Exiting children will be "popped" out of the page layout. This allows surrounding elements to move to their new layout immediately. ### `onExitComplete` Fires when all exiting nodes have completed animating out. ## Troubleshooting ### Exit animations aren't working Ensure all **immediate** children get a unique `key` prop that **remains the same for that component every render**. For instance, providing `index` as a `key` is **bad** because if the items reorder then the `index` will not be matched to the `item`: <AnimatePresence\> <Component v-for\="(item,index) in items" :key\="index" /> </AnimatePresence\> It's preferred to pass something that's unique to that item, for instance an ID: <AnimatePresence\> <Componen v-for\="(item,index) in items" :key\="item.id" /> </AnimatePresence\> Also make sure `AnimatePresence` is **outside** of the code that unmounts the element. If `AnimatePresence` itself unmounts, then it can't control exit animations! For example, this will **not work**: <AnimatePresence v-if\="isVisible"\> <Component /> </AnimatePresence\> Instead, the conditional should be at the root of `AnimatePresence`: <AnimatePresence\> <Component v-if\="isVisible" /> </AnimatePresence\> ### Layout animations not working with `mode="sync"` When mixing layout and exit animations, it might be necessary to wrap the group in `LayoutGroup` to ensure that components outside of `AnimatePresence` know when to perform a layout animation. <LayoutGroup\> <motion.ul layout\> <AnimatePresence\> <motion.li v-for\="item in items" layout :key\="item.id" /> </AnimatePresence\> </motion.ul\> </LayoutGroup\> ### Layout animations not working with `mode="popLayout"` When any HTML element has an active `transform` it temporarily becomes the offset parent of its children. This can cause children with `position: "absolute"` not to appear where you expect. `mode="popLayout"` works by using `position: "absolute"`. So to ensure consistent and expected positioning during a layout animation, ensure that the animating parent has a `position` other than `"static"`. <motion.ul layout :style\="{ position: 'relative' }"\> <AnimatePresence mode\="popLayout"\> <motion.li v-for\="item in items" layout :key\="item.id" /> </AnimatePresence\> </motion.ul\> `AnimatePresence` makes exit animations easy. By wrapping one or more `motion` components with `AnimatePresence`, we gain access to the `exit` animation prop. <AnimatePresence\> <motion.div v-if\="show" key\="modal" :exit\="{ opacity: 0 }" />} </AnimatePresence\> ## Usage ### Import import { AnimatePresence } from "motion-v" ### Exit animations `AnimatePresence` works by detecting when its **direct children** are removed from the Vue tree. This can be due to a component mounting/remounting: <AnimatePresence\> <Modal v-if\="show" key\="modal" /> </AnimatePresence\> or using `v-show`: <AnimatePresence\> <Modal v-show\="show" key\="modal" /> </AnimatePresence\> Or Its `key` changing: <AnimatePresence\> <Slide :key\="activeItem.id" /> </AnimatePresence\> Or when children in a list are added/removed: <AnimatePresence\> <motion.li v-for\="item in items" :key\="item.id" :exit\="{ opacity: 1 }" layout /> </AnimatePresence\> Any `motion` components within the exiting component will fire animations defined on their `exit` props before the component is removed from the DOM. <template\> <motion.div :exit\="{ opacity: 0 }"\> <img :src\="img.src" /> <motion.p :exit\="{ y: 10 }"\>{{description}}</motion.p\> </motion.div\> </template\> **Note:** Direct children must each have a unique `key` prop so `AnimatePresence` can track their presence in the tree. Like `initial` and `animate`, `exit` can be defined either as an object of values, or as a variant label. <script setup\> const modalVariants = { visible: { opacity: 1, transition: { when: "beforeChildren" } }, hidden: { opacity: 0, transition: { when: "afterChildren" } } } </script\> <template\> <motion.div :variants\="modalVariants" initial\="hidden" animate\="visible" exit\="hidden" \> {{children}} </motion.div\> </template\> ### Changing `key` Changing a `key` prop makes Vue create an entirely new component. So by changing the `key` of a single child of `AnimatePresence`, we can easily make components like slideshows. <template\> <AnimatePresence\> <motion.img :key\="image.src" :src\="image.src" :initial\="{ x: 300, opacity: 0 }" :animate\="{ x: 0, opacity: 1 }" :exit\="{ x: -300, opacity: 0 }" /> </AnimatePresence\> </template\> ## Props ### `initial` By passing `:initial="false"`, `AnimatePresence` will disable any initial animations on children that are present when the component is first rendered. <AnimatePresence :initial\="false"\> <Slide :key\="activeItem.id" /> </AnimatePresence\> ### `custom` When a component is removed, there's no longer a chance to update its props (because it's no longer in the Vue tree). Therefore we can't update its exit animation with the same render that removed the component. By passing a value through `AnimatePresence`'s `custom` prop, we can use dynamic variants to change the `exit` animation. <script setup\> const variants = { hidden: (direction) \=> ({ opacity: 0, x: direction === 1 ? -300 : 300 }), visible: { opacity: 1, x: 0 } } </script\> <template\> <AnimatePresence :custom\="direction"\> <motion.img :key\="image.src" :src\="image.src" :variants\="variants" initial\="hidden" animate\="visible" exit\="hidden" /> </AnimatePresence\> </template\> ### `mode` **Default:** `"sync"` Decides how `AnimatePresence` handles entering and exiting children. * `"sync"`: Children animate in/out as soon as they're added/removed. * `"wait"`: The entering child will wait until the exiting child has animated out. **Note:** Currently only renders a single child at a time. * `"popLayout"`: Exiting children will be "popped" out of the page layout. This allows surrounding elements to move to their new layout immediately. ### `onExitComplete` Fires when all exiting nodes have completed animating out. ## Troubleshooting ### Exit animations aren't working Ensure all **immediate** children get a unique `key` prop that **remains the same for that component every render**. For instance, providing `index` as a `key` is **bad** because if the items reorder then the `index` will not be matched to the `item`: <AnimatePresence\> <Component v-for\="(item,index) in items" :key\="index" /> </AnimatePresence\> It's preferred to pass something that's unique to that item, for instance an ID: <AnimatePresence\> <Componen v-for\="(item,index) in items" :key\="item.id" /> </AnimatePresence\> Also make sure `AnimatePresence` is **outside** of the code that unmounts the element. If `AnimatePresence` itself unmounts, then it can't control exit animations! For example, this will **not work**: <AnimatePresence v-if\="isVisible"\> <Component /> </AnimatePresence\> Instead, the conditional should be at the root of `AnimatePresence`: <AnimatePresence\> <Component v-if\="isVisible" /> </AnimatePresence\> ### Layout animations not working with `mode="sync"` When mixing layout and exit animations, it might be necessary to wrap the group in `LayoutGroup` to ensure that components outside of `AnimatePresence` know when to perform a layout animation. <LayoutGroup\> <motion.ul layout\> <AnimatePresence\> <motion.li v-for\="item in items" layout :key\="item.id" /> </AnimatePresence\> </motion.ul\> </LayoutGroup\> ### Layout animations not working with `mode="popLayout"` When any HTML element has an active `transform` it temporarily becomes the offset parent of its children. This can cause children with `position: "absolute"` not to appear where you expect. `mode="popLayout"` works by using `position: "absolute"`. So to ensure consistent and expected positioning during a layout animation, ensure that the animating parent has a `position` other than `"static"`. <motion.ul layout :style\="{ position: 'relative' }"\> <AnimatePresence mode\="popLayout"\> <motion.li v-for\="item in items" layout :key\="item.id" /> </AnimatePresence\> </motion.ul\> --- ## Page: https://motion.dev/docs/vue-animate-number # AnimateNumber Motion+ Exclusive Checking Motion+ status… This content is exclusive to Motion+ members Get Motion+ for instant access One-time payment, no subscription Already joined? Login Checking Motion+ status… This content is exclusive to Motion+ members Get Motion+ for instant access One-time payment, no subscription Already joined? Login Checking Motion+ status… This content is exclusive to Motion+ members Get Motion+ for instant access One-time payment, no subscription Already joined? Login `AnimateNumber` creates beautiful number animations with Motion. <AnimateNumber :value\="count"/> You can create a number of popular animation effects, like countdowns, engagement bars, or labelling user inputs. It's a continuation of the original version of Max Barvian's NumberFlow component, which was built on Motion. Because `AnimateNumber` leverages Motion's existing layout animations, it's only 3.6kb on top of Motion for Vue. It also means you can use Motion's existing transition settings. However, being based on an older version of NumberFlow means it currently lacks a couple of its newer props like `trend` and `plugins`. In this guide, we'll learn how to install `AnimateNumber` into our projects, and how to use it for a variety of number animation effects. --- ## Page: https://motion.dev/docs/vue-cursor # Cursor Motion+ Exclusive Checking Motion+ status… This content is exclusive to Motion+ members Get Motion+ for instant access One-time payment, no subscription Already joined? Login Checking Motion+ status… This content is exclusive to Motion+ members Get Motion+ for instant access One-time payment, no subscription Already joined? Login Checking Motion+ status… This content is exclusive to Motion+ members Get Motion+ for instant access One-time payment, no subscription Already joined? Login Cursor is a creative cursor component for Vue. It makes it easy to build custom cursor and follow cursor effects. With its default settings, it replaces the browser cursor with a dynamic cursor. <Cursor /> This cursor automatically detects the types of content it's hovering over. When hovering a link or button, it grows. When it's pressed, it shrinks. It also detects `disabled` status. When hovering selectable text, it transforms into a text selector that grows with the size of the text. The cursor can be fully styled with CSS and animated using Motion's variants. Beyond that, it's also possible to set custom content when hovering over specific elements. With only a prop, we can create a follow cursor effect. Great for previews or popup information. <Cursor follow /> We can also render as many cursors as we like, all at the same time. Attaching them to the cursor with springs of varying strengths. In this guide, we'll learn how to install `Cursor`, customise it with its various options. --- ## Page: https://motion.dev/docs/vue-layout-group # LayoutGroup `motion` components with a `layout` prop will detect and animate layout changes every time they commit a Vue re-render, or their `layoutDependency` prop changes. `LayoutGroup` is used to group components that might not render together but do affect each-other's state. ## Usage Take these accordion items that each handle their own state: <script setup\> const isOpen = ref(false) </script\> <template\> <motion.div layout @click\="isOpen\=!isOpen" \> <motion.h2 layout\>{{header}}</motion.h2\> {{isOpen ? content : null}} </motion.div\> </template\> If we arrange these next to each other in an `Accordion`, when their state updates, their siblings have no way of knowing: <!-- Accordion --> <template\> <ToggleContent /> <ToggleContent /> </template\> This can be fixed by grouping both components with `LayoutGroup`: <!-- Accordion --> <template\> <LayoutGroup\> <ToggleContent /> <ToggleContent /> </LayoutGroup\> </template\> ### Namespace `layoutId` Components expecting to perform shared layout animations are provided a `layoutId` prop. In this following example, each `Tab` renders an element with the `layoutId="underline"` prop. <!-- Tab --> <template\> <li\> {{label}} <motion.div v-if\="isSelected" layoutId\="underline" /> </li\> </template\> <!-- TabRow --> <template\> <Tab v-for\="item in items" :key\="item.id" v-bind\="item"/> </template\> `layoutId` is global across your site. So to render multiple `TabRow`s we want to group them with `LayoutGroup` and `id` prop: <!-- TabRow --> <template\> <LayoutGroup :id\="id"\> <Tab v-for\="item in items" :key\="item.id" v-bind\="item"/> </LayoutGroup\> </template\> `motion` components with a `layout` prop will detect and animate layout changes every time they commit a Vue re-render, or their `layoutDependency` prop changes. `LayoutGroup` is used to group components that might not render together but do affect each-other's state. ## Usage Take these accordion items that each handle their own state: <script setup\> const isOpen = ref(false) </script\> <template\> <motion.div layout @click\="isOpen\=!isOpen" \> <motion.h2 layout\>{{header}}</motion.h2\> {{isOpen ? content : null}} </motion.div\> </template\> If we arrange these next to each other in an `Accordion`, when their state updates, their siblings have no way of knowing: <!-- Accordion --> <template\> <ToggleContent /> <ToggleContent /> </template\> This can be fixed by grouping both components with `LayoutGroup`: <!-- Accordion --> <template\> <LayoutGroup\> <ToggleContent /> <ToggleContent /> </LayoutGroup\> </template\> ### Namespace `layoutId` Components expecting to perform shared layout animations are provided a `layoutId` prop. In this following example, each `Tab` renders an element with the `layoutId="underline"` prop. <!-- Tab --> <template\> <li\> {{label}} <motion.div v-if\="isSelected" layoutId\="underline" /> </li\> </template\> <!-- TabRow --> <template\> <Tab v-for\="item in items" :key\="item.id" v-bind\="item"/> </template\> `layoutId` is global across your site. So to render multiple `TabRow`s we want to group them with `LayoutGroup` and `id` prop: <!-- TabRow --> <template\> <LayoutGroup :id\="id"\> <Tab v-for\="item in items" :key\="item.id" v-bind\="item"/> </LayoutGroup\> </template\> `motion` components with a `layout` prop will detect and animate layout changes every time they commit a Vue re-render, or their `layoutDependency` prop changes. `LayoutGroup` is used to group components that might not render together but do affect each-other's state. ## Usage Take these accordion items that each handle their own state: <script setup\> const isOpen = ref(false) </script\> <template\> <motion.div layout @click\="isOpen\=!isOpen" \> <motion.h2 layout\>{{header}}</motion.h2\> {{isOpen ? content : null}} </motion.div\> </template\> If we arrange these next to each other in an `Accordion`, when their state updates, their siblings have no way of knowing: <!-- Accordion --> <template\> <ToggleContent /> <ToggleContent /> </template\> This can be fixed by grouping both components with `LayoutGroup`: <!-- Accordion --> <template\> <LayoutGroup\> <ToggleContent /> <ToggleContent /> </LayoutGroup\> </template\> ### Namespace `layoutId` Components expecting to perform shared layout animations are provided a `layoutId` prop. In this following example, each `Tab` renders an element with the `layoutId="underline"` prop. <!-- Tab --> <template\> <li\> {{label}} <motion.div v-if\="isSelected" layoutId\="underline" /> </li\> </template\> <!-- TabRow --> <template\> <Tab v-for\="item in items" :key\="item.id" v-bind\="item"/> </template\> `layoutId` is global across your site. So to render multiple `TabRow`s we want to group them with `LayoutGroup` and `id` prop: <!-- TabRow --> <template\> <LayoutGroup :id\="id"\> <Tab v-for\="item in items" :key\="item.id" v-bind\="item"/> </LayoutGroup\> </template\> --- ## Page: https://motion.dev/docs/vue-motion-config # MotionConfig The `MotionConfig` component can be used to set configuration options for all child `motion` components. <script setup\> import { motion, MotionConfig } from "motion-v" </script\> <template\> <MotionConfig :transition\="{ duration: 1 }"\> <motion.div :initial\="{ opacity: 0 }" :animate\="{ opacity: 1 }" /> </MotionConfig\> </template\> ## Props ### `transition` Define a fallback `transition` to use for all child `motion` components. ### `reducedMotion` **Default:** `"never"` `reducedMotion` lets you set a site-wide policy for handling reduced motion. It offers the following options: * `"user"`: Respect the user's device setting. * `"always"`: Enforce reduced motion (useful for debugging). * `"never"`: Don't respect reduced motion. When reduced motion is on, transform and layout animations will be disabled. Other animations, like `opacity` and `backgroundColor`, will persist. ### `nonce` If using a Content Security Policy with a `nonce` attribute, passing the same attribute through `MotionConfig` will allow any `style` blocks generated by Motion to adhere the the security policy. The `MotionConfig` component can be used to set configuration options for all child `motion` components. <script setup\> import { motion, MotionConfig } from "motion-v" </script\> <template\> <MotionConfig :transition\="{ duration: 1 }"\> <motion.div :initial\="{ opacity: 0 }" :animate\="{ opacity: 1 }" /> </MotionConfig\> </template\> ## Props ### `transition` Define a fallback `transition` to use for all child `motion` components. ### `reducedMotion` **Default:** `"never"` `reducedMotion` lets you set a site-wide policy for handling reduced motion. It offers the following options: * `"user"`: Respect the user's device setting. * `"always"`: Enforce reduced motion (useful for debugging). * `"never"`: Don't respect reduced motion. When reduced motion is on, transform and layout animations will be disabled. Other animations, like `opacity` and `backgroundColor`, will persist. ### `nonce` If using a Content Security Policy with a `nonce` attribute, passing the same attribute through `MotionConfig` will allow any `style` blocks generated by Motion to adhere the the security policy. The `MotionConfig` component can be used to set configuration options for all child `motion` components. <script setup\> import { motion, MotionConfig } from "motion-v" </script\> <template\> <MotionConfig :transition\="{ duration: 1 }"\> <motion.div :initial\="{ opacity: 0 }" :animate\="{ opacity: 1 }" /> </MotionConfig\> </template\> ## Props ### `transition` Define a fallback `transition` to use for all child `motion` components. ### `reducedMotion` **Default:** `"never"` `reducedMotion` lets you set a site-wide policy for handling reduced motion. It offers the following options: * `"user"`: Respect the user's device setting. * `"always"`: Enforce reduced motion (useful for debugging). * `"never"`: Don't respect reduced motion. When reduced motion is on, transform and layout animations will be disabled. Other animations, like `opacity` and `backgroundColor`, will persist. ### `nonce` If using a Content Security Policy with a `nonce` attribute, passing the same attribute through `MotionConfig` will allow any `style` blocks generated by Motion to adhere the the security policy. --- ## Page: https://motion.dev/docs/vue-motion-value # Motion values Motion values track the state and velocity of animated values. They are composable, signal-like values that are performant because Motion can render them with its optimised DOM renderer. Usually, these are created automatically by `motion` components. But for advanced use cases, it's possible to create them manually. <script setup\> import { motion, useMotionValue } from "motion-v" const x = useMotionValue(0) </script\> <template\> <motion.div :style\="{ x }" /> </template\> By manually creating motion values you can: * Set and get their state. * Pass to multiple components to synchronise motion across them. * Chain `MotionValue`s via the `useTransform` hook. * Update visual properties without triggering Vue's render cycle. * Subscribe to updates. <script setup\> import { useMotionValue, useTransform} from "motion-v" const x = useMotionValue(0) const opacity = useTransform( x, \[\-200, 0, 200\], \[0, 1, 0\] ) </script\> <template\> <!-- // Will change opacity as element is dragged left/right --> <motion.div drag\="x" :style\="{ x, opacity }" /> </template\> ## Usage Motion values can be created with the `useMotionValue` hook. The string or number passed to `useMotionValue` will act as its initial state. import { useMotionValue } from "motion-v" const x = useMotionValue(0) Motion values can be passed to a `motion` component via `style`: <motion.li :style\="{ x }" /> Or for SVG attributes, via the attribute prop itself: <motion.circle :cx\="cx" /> It's possible to pass the same motion value to multiple components. Motion values can be updated with the `set` method. x.set(100) Changes to the motion value will update the DOM **without triggering a Vue re-render**. Motion values can be updated multiple times but renders will be batched to the next animation frame. A motion value can hold any string or number. We can read it with the `get` method. x.get() // 100 Motion values containing a number can return a velocity via the `getVelocity` method. This returns the velocity as calculated **per second** to account for variations in frame rate across devices. const xVelocity = x.getVelocity() For strings and colors, `getVelocity` will always return `0`. ### Events Listeners can be added to motion values via the `on` method or the `useMotionValueEvent` hook. useMotionValueEvent(x, "change", (latest) \=> console.log(latest)) Available events are `"change"`, `"animationStart"`, `"animationComplete"` `"animationCancel"`. ### Composition Beyond `useMotionValue`, Motion provides a number of hooks for creating and composing motion values, like `useSpring` and `useTransform`. For example, with `useTransform` we can take the latest state of one or more motion values and create a new motion value with the result. const y = useTransform(() \=> x.get() \* 2) `useSpring` can make a motion value that's attached to another via a spring. const dragX = useMotionValue(0) const dragY = useMotionValue(0) const x = useSpring(dragX) const y = useSpring(dragY) These motion values can then go on to be passed to `motion` components, or composed with more hooks like `useVelocity`. ## API ### `get()` Returns the latest state of the motion value. ### `getVelocity()` Returns the latest velocity of the motion value. Returns `0` if the value is non-numerical. ### `set()` Sets the motion value to a new state. x.set("#f00") ### `jump()` Jumps the motion value to a new state in a way that breaks continuity from previous values: * Resets `velocity` to `0`. * Ends active animations. * Ignores attached effects (for instance `useSpring`'s spring). const x = useSpring(0) x.jump(10) x.getVelocity() // 0 ### `isAnimating()` Returns `true` if the value is currently animating. ### `stop()` Stop the active animation. ### `on()` Subscribe to motion value events. Available events are: * `change` * `animationStart` * `animationCancel` * `animationComplete` It returns a function that, when called, will unsubscribe the listener. const unsubscribe = x.on("change", latest \=> console.log(latest)) ### `destroy()` Destroy and clean up subscribers to this motion value. This is normally handled automatically, so this method is only necessary if you've manually created a motion value outside the Vue render cycle using the vanilla `motionValue` hook. Motion values track the state and velocity of animated values. They are composable, signal-like values that are performant because Motion can render them with its optimised DOM renderer. Usually, these are created automatically by `motion` components. But for advanced use cases, it's possible to create them manually. <script setup\> import { motion, useMotionValue } from "motion-v" const x = useMotionValue(0) </script\> <template\> <motion.div :style\="{ x }" /> </template\> By manually creating motion values you can: * Set and get their state. * Pass to multiple components to synchronise motion across them. * Chain `MotionValue`s via the `useTransform` hook. * Update visual properties without triggering Vue's render cycle. * Subscribe to updates. <script setup\> import { useMotionValue, useTransform} from "motion-v" const x = useMotionValue(0) const opacity = useTransform( x, \[\-200, 0, 200\], \[0, 1, 0\] ) </script\> <template\> <!-- // Will change opacity as element is dragged left/right --> <motion.div drag\="x" :style\="{ x, opacity }" /> </template\> ## Usage Motion values can be created with the `useMotionValue` hook. The string or number passed to `useMotionValue` will act as its initial state. import { useMotionValue } from "motion-v" const x = useMotionValue(0) Motion values can be passed to a `motion` component via `style`: <motion.li :style\="{ x }" /> Or for SVG attributes, via the attribute prop itself: <motion.circle :cx\="cx" /> It's possible to pass the same motion value to multiple components. Motion values can be updated with the `set` method. x.set(100) Changes to the motion value will update the DOM **without triggering a Vue re-render**. Motion values can be updated multiple times but renders will be batched to the next animation frame. A motion value can hold any string or number. We can read it with the `get` method. x.get() // 100 Motion values containing a number can return a velocity via the `getVelocity` method. This returns the velocity as calculated **per second** to account for variations in frame rate across devices. const xVelocity = x.getVelocity() For strings and colors, `getVelocity` will always return `0`. ### Events Listeners can be added to motion values via the `on` method or the `useMotionValueEvent` hook. useMotionValueEvent(x, "change", (latest) \=> console.log(latest)) Available events are `"change"`, `"animationStart"`, `"animationComplete"` `"animationCancel"`. ### Composition Beyond `useMotionValue`, Motion provides a number of hooks for creating and composing motion values, like `useSpring` and `useTransform`. For example, with `useTransform` we can take the latest state of one or more motion values and create a new motion value with the result. const y = useTransform(() \=> x.get() \* 2) `useSpring` can make a motion value that's attached to another via a spring. const dragX = useMotionValue(0) const dragY = useMotionValue(0) const x = useSpring(dragX) const y = useSpring(dragY) These motion values can then go on to be passed to `motion` components, or composed with more hooks like `useVelocity`. ## API ### `get()` Returns the latest state of the motion value. ### `getVelocity()` Returns the latest velocity of the motion value. Returns `0` if the value is non-numerical. ### `set()` Sets the motion value to a new state. x.set("#f00") ### `jump()` Jumps the motion value to a new state in a way that breaks continuity from previous values: * Resets `velocity` to `0`. * Ends active animations. * Ignores attached effects (for instance `useSpring`'s spring). const x = useSpring(0) x.jump(10) x.getVelocity() // 0 ### `isAnimating()` Returns `true` if the value is currently animating. ### `stop()` Stop the active animation. ### `on()` Subscribe to motion value events. Available events are: * `change` * `animationStart` * `animationCancel` * `animationComplete` It returns a function that, when called, will unsubscribe the listener. const unsubscribe = x.on("change", latest \=> console.log(latest)) ### `destroy()` Destroy and clean up subscribers to this motion value. This is normally handled automatically, so this method is only necessary if you've manually created a motion value outside the Vue render cycle using the vanilla `motionValue` hook. Motion values track the state and velocity of animated values. They are composable, signal-like values that are performant because Motion can render them with its optimised DOM renderer. Usually, these are created automatically by `motion` components. But for advanced use cases, it's possible to create them manually. <script setup\> import { motion, useMotionValue } from "motion-v" const x = useMotionValue(0) </script\> <template\> <motion.div :style\="{ x }" /> </template\> By manually creating motion values you can: * Set and get their state. * Pass to multiple components to synchronise motion across them. * Chain `MotionValue`s via the `useTransform` hook. * Update visual properties without triggering Vue's render cycle. * Subscribe to updates. <script setup\> import { useMotionValue, useTransform} from "motion-v" const x = useMotionValue(0) const opacity = useTransform( x, \[\-200, 0, 200\], \[0, 1, 0\] ) </script\> <template\> <!-- // Will change opacity as element is dragged left/right --> <motion.div drag\="x" :style\="{ x, opacity }" /> </template\> ## Usage Motion values can be created with the `useMotionValue` hook. The string or number passed to `useMotionValue` will act as its initial state. import { useMotionValue } from "motion-v" const x = useMotionValue(0) Motion values can be passed to a `motion` component via `style`: <motion.li :style\="{ x }" /> Or for SVG attributes, via the attribute prop itself: <motion.circle :cx\="cx" /> It's possible to pass the same motion value to multiple components. Motion values can be updated with the `set` method. x.set(100) Changes to the motion value will update the DOM **without triggering a Vue re-render**. Motion values can be updated multiple times but renders will be batched to the next animation frame. A motion value can hold any string or number. We can read it with the `get` method. x.get() // 100 Motion values containing a number can return a velocity via the `getVelocity` method. This returns the velocity as calculated **per second** to account for variations in frame rate across devices. const xVelocity = x.getVelocity() For strings and colors, `getVelocity` will always return `0`. ### Events Listeners can be added to motion values via the `on` method or the `useMotionValueEvent` hook. useMotionValueEvent(x, "change", (latest) \=> console.log(latest)) Available events are `"change"`, `"animationStart"`, `"animationComplete"` `"animationCancel"`. ### Composition Beyond `useMotionValue`, Motion provides a number of hooks for creating and composing motion values, like `useSpring` and `useTransform`. For example, with `useTransform` we can take the latest state of one or more motion values and create a new motion value with the result. const y = useTransform(() \=> x.get() \* 2) `useSpring` can make a motion value that's attached to another via a spring. const dragX = useMotionValue(0) const dragY = useMotionValue(0) const x = useSpring(dragX) const y = useSpring(dragY) These motion values can then go on to be passed to `motion` components, or composed with more hooks like `useVelocity`. ## API ### `get()` Returns the latest state of the motion value. ### `getVelocity()` Returns the latest velocity of the motion value. Returns `0` if the value is non-numerical. ### `set()` Sets the motion value to a new state. x.set("#f00") ### `jump()` Jumps the motion value to a new state in a way that breaks continuity from previous values: * Resets `velocity` to `0`. * Ends active animations. * Ignores attached effects (for instance `useSpring`'s spring). const x = useSpring(0) x.jump(10) x.getVelocity() // 0 ### `isAnimating()` Returns `true` if the value is currently animating. ### `stop()` Stop the active animation. ### `on()` Subscribe to motion value events. Available events are: * `change` * `animationStart` * `animationCancel` * `animationComplete` It returns a function that, when called, will unsubscribe the listener. const unsubscribe = x.on("change", latest \=> console.log(latest)) ### `destroy()` Destroy and clean up subscribers to this motion value. This is normally handled automatically, so this method is only necessary if you've manually created a motion value outside the Vue render cycle using the vanilla `motionValue` hook. --- ## Page: https://motion.dev/docs/vue-use-motion-template # useMotionTemplate `useMotionTemplate` creates a new motion value from a string template containing other motion values. const x = useMotionValue(100) const transform = useMotionTemplate\`transform(${x}px)\` Whenever a motion value within the string template updates, the returned motion value will update with the latest value. ## Usage Import from Motion: import { useMotionTemplate } from "motion-v" `useMotionTemplate` is a "tagged template", so rather than being called like a normal function, it's called as a string template: useMotionValue\`\` This string template can accept both text and other motion values: <script setup\> const blur = useMotionValue(10) const saturate = useMotionValue(50) const filter = useMotionTemplate\`blur(${10}px) saturate(${saturate}%)\` </script\> <template\> <motion.div :style\="{ filter }" /> </template\> The latest value of the returned motion value will be the string template with each provided motion value replaced with its latest value. <script setup\> const shadowX = useSpring(0) const shadowY = useMotionValue(0) const filter = useMotionTemplate\`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))\` </script\> <template\> <motion.div :style\="{ filter }" /> </template\> `useMotionTemplate` creates a new motion value from a string template containing other motion values. const x = useMotionValue(100) const transform = useMotionTemplate\`transform(${x}px)\` Whenever a motion value within the string template updates, the returned motion value will update with the latest value. ## Usage Import from Motion: import { useMotionTemplate } from "motion-v" `useMotionTemplate` is a "tagged template", so rather than being called like a normal function, it's called as a string template: useMotionValue\`\` This string template can accept both text and other motion values: <script setup\> const blur = useMotionValue(10) const saturate = useMotionValue(50) const filter = useMotionTemplate\`blur(${10}px) saturate(${saturate}%)\` </script\> <template\> <motion.div :style\="{ filter }" /> </template\> The latest value of the returned motion value will be the string template with each provided motion value replaced with its latest value. <script setup\> const shadowX = useSpring(0) const shadowY = useMotionValue(0) const filter = useMotionTemplate\`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))\` </script\> <template\> <motion.div :style\="{ filter }" /> </template\> `useMotionTemplate` creates a new motion value from a string template containing other motion values. const x = useMotionValue(100) const transform = useMotionTemplate\`transform(${x}px)\` Whenever a motion value within the string template updates, the returned motion value will update with the latest value. ## Usage Import from Motion: import { useMotionTemplate } from "motion-v" `useMotionTemplate` is a "tagged template", so rather than being called like a normal function, it's called as a string template: useMotionValue\`\` This string template can accept both text and other motion values: <script setup\> const blur = useMotionValue(10) const saturate = useMotionValue(50) const filter = useMotionTemplate\`blur(${10}px) saturate(${saturate}%)\` </script\> <template\> <motion.div :style\="{ filter }" /> </template\> The latest value of the returned motion value will be the string template with each provided motion value replaced with its latest value. <script setup\> const shadowX = useSpring(0) const shadowY = useMotionValue(0) const filter = useMotionTemplate\`drop-shadow(${shadowX}px ${shadowY}px 20px rgba(0,0,0,0.3))\` </script\> <template\> <motion.div :style\="{ filter }" /> </template\> --- ## Page: https://motion.dev/docs/vue-use-motion-value-event # useMotionValueEvent `useMotionValueEvent` manages a motion value event handler throughout the lifecycle of a Vue component. <script setup\> import { useMotionValue, useMotionValueEvent } from "motion-v" const x = useMotionValue(0) useMotionValueEvent(x, "animationStart", () \=> { console.log("animation started on x") }) useMotionValueEvent(x, "change", (latest) \=> { console.log("x changed to", latest) }) </script\> <template\> <motion.div :style\="{ x }" /> </template\> When the component is unmounted, event handlers will be safely cleaned up. ## Usage Import from Motion: import { useMotionValueEvent } from "motion-v" To add an event listener to a motion value, provide the value, event name and callback: const color = useMotionValue("#00f") useMotionValueEvent(color, "change", (latest) \=> { console.log(latest) }) Available events are: * `change` * `animationStart` * `animationComplete` * `animationCancel` `"change"` events are provided the latest value of the motion value. ### Advanced `useMotionValueEvent` is a helper function for a motion value's `on` method. With `on`, you can start listening to events whenever you like, for instance within an event handler. But remember to also unsubscribe when the component unmounts. watch(\[x, y\],(n,o,onCleanUp) \=> { const doSomething = () \=> {} const unsubX = x.on("change", doSomething) const unsubY = y.on("change", doSomething) onCleanUp(() \=> { unsubX() unsubY() }) },{ immediate:true }) `useMotionValueEvent` manages a motion value event handler throughout the lifecycle of a Vue component. <script setup\> import { useMotionValue, useMotionValueEvent } from "motion-v" const x = useMotionValue(0) useMotionValueEvent(x, "animationStart", () \=> { console.log("animation started on x") }) useMotionValueEvent(x, "change", (latest) \=> { console.log("x changed to", latest) }) </script\> <template\> <motion.div :style\="{ x }" /> </template\> When the component is unmounted, event handlers will be safely cleaned up. ## Usage Import from Motion: import { useMotionValueEvent } from "motion-v" To add an event listener to a motion value, provide the value, event name and callback: const color = useMotionValue("#00f") useMotionValueEvent(color, "change", (latest) \=> { console.log(latest) }) Available events are: * `change` * `animationStart` * `animationComplete` * `animationCancel` `"change"` events are provided the latest value of the motion value. ### Advanced `useMotionValueEvent` is a helper function for a motion value's `on` method. With `on`, you can start listening to events whenever you like, for instance within an event handler. But remember to also unsubscribe when the component unmounts. watch(\[x, y\],(n,o,onCleanUp) \=> { const doSomething = () \=> {} const unsubX = x.on("change", doSomething) const unsubY = y.on("change", doSomething) onCleanUp(() \=> { unsubX() unsubY() }) },{ immediate:true }) `useMotionValueEvent` manages a motion value event handler throughout the lifecycle of a Vue component. <script setup\> import { useMotionValue, useMotionValueEvent } from "motion-v" const x = useMotionValue(0) useMotionValueEvent(x, "animationStart", () \=> { console.log("animation started on x") }) useMotionValueEvent(x, "change", (latest) \=> { console.log("x changed to", latest) }) </script\> <template\> <motion.div :style\="{ x }" /> </template\> When the component is unmounted, event handlers will be safely cleaned up. ## Usage Import from Motion: import { useMotionValueEvent } from "motion-v" To add an event listener to a motion value, provide the value, event name and callback: const color = useMotionValue("#00f") useMotionValueEvent(color, "change", (latest) \=> { console.log(latest) }) Available events are: * `change` * `animationStart` * `animationComplete` * `animationCancel` `"change"` events are provided the latest value of the motion value. ### Advanced `useMotionValueEvent` is a helper function for a motion value's `on` method. With `on`, you can start listening to events whenever you like, for instance within an event handler. But remember to also unsubscribe when the component unmounts. watch(\[x, y\],(n,o,onCleanUp) \=> { const doSomething = () \=> {} const unsubX = x.on("change", doSomething) const unsubY = y.on("change", doSomething) onCleanUp(() \=> { unsubX() unsubY() }) },{ immediate:true }) --- ## Page: https://motion.dev/docs/vue-use-spring # useSpring `useSpring` creates a motion value that will animate to its latest target with a spring animation. The target can either be set manually via `.set`, or automatically by passing in another motion value. ## Usage Import `useSpring` from Motion: import { useSpring } from "motion-v" ### Direct control `useSpring` can be created with a number, or a unit-type (`px`, `%` etc) string: const x = useSpring(0) const y = useSpring("100vh") Now, whenever this motion value is updated via `set()`, the value will animate to its new target with the defined spring. x.set(100) y.set("50vh") It's also possible to update this value immediately, without a spring, with the `jump()` method. x.jump(50) y.jump("0vh") ### Track another motion value Its also possible to automatically spring towards the latest value of another motion value: const x = useMotionValue(0) const y = useSpring(x) This source motion value must also be a number, or unit-type string. ### Transition The type of `spring` can be defined with the usual spring transition option. useSpring(0, { stiffness: 300 }) `useSpring` creates a motion value that will animate to its latest target with a spring animation. The target can either be set manually via `.set`, or automatically by passing in another motion value. ## Usage Import `useSpring` from Motion: import { useSpring } from "motion-v" ### Direct control `useSpring` can be created with a number, or a unit-type (`px`, `%` etc) string: const x = useSpring(0) const y = useSpring("100vh") Now, whenever this motion value is updated via `set()`, the value will animate to its new target with the defined spring. x.set(100) y.set("50vh") It's also possible to update this value immediately, without a spring, with the `jump()` method. x.jump(50) y.jump("0vh") ### Track another motion value Its also possible to automatically spring towards the latest value of another motion value: const x = useMotionValue(0) const y = useSpring(x) This source motion value must also be a number, or unit-type string. ### Transition The type of `spring` can be defined with the usual spring transition option. useSpring(0, { stiffness: 300 }) `useSpring` creates a motion value that will animate to its latest target with a spring animation. The target can either be set manually via `.set`, or automatically by passing in another motion value. ## Usage Import `useSpring` from Motion: import { useSpring } from "motion-v" ### Direct control `useSpring` can be created with a number, or a unit-type (`px`, `%` etc) string: const x = useSpring(0) const y = useSpring("100vh") Now, whenever this motion value is updated via `set()`, the value will animate to its new target with the defined spring. x.set(100) y.set("50vh") It's also possible to update this value immediately, without a spring, with the `jump()` method. x.jump(50) y.jump("0vh") ### Track another motion value Its also possible to automatically spring towards the latest value of another motion value: const x = useMotionValue(0) const y = useSpring(x) This source motion value must also be a number, or unit-type string. ### Transition The type of `spring` can be defined with the usual spring transition option. useSpring(0, { stiffness: 300 }) --- ## Page: https://motion.dev/docs/vue-use-scroll # useScroll `useScroll` is used to create scroll-linked animations, like progress indicators and parallax effects. <script setup\> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> ## Usage Import `useScroll` from Motion: import { useScroll } from "motion-v" `useScroll` returns four motion values: * `scrollX`/`Y`: The absolute scroll position, in pixels. * `scrollXProgress`/`YProgress`: The scroll position between the defined offsets, as a value between `0` and `1`. ### Page scroll By default, useScroll tracks the page scroll. const { scrollY } = useScroll() useMotionValueEvent(scrollY, "change", (latest) \=> { console.log("Page scroll: ", latest) }) For example, we could show a page scroll indicator by passing `scrollYProgress` straight to the `scaleX` style of a progress bar. <script setup\> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> As `useScroll` returns motion values, we can compose this scroll info with other motion value hooks like `useTransform` and `useSpring`: <script setup\> const { scrollYProgress } = useScroll() const scaleX = useSpring(scrollYProgress) </script\> <template\> <Motion :style\="{ scaleX }" /> </template\> > Since `scrollY` is a `MotionValue`, there's a neat trick you can use to tell when the user's scroll direction changes: > > <script setup\> > const { scrollY } = useScroll() > const scrollDirection = ref('down') > > useMotionValueEvent(scrollY, 'change', (current) \=> { > const diff = current - scrollY.getPrevious() > scrollDirection.value = diff > 0 ? 'down' : 'up' > }) > </script\> > > Perfect for triggering a sticky header animation! > > ~ Sam Selikoff, Motion for React Recipes ### Element scroll To track the scroll position of a scrollable element we can pass the element's `ref` to `useScroll`'s `container` option: <script setup\> const carouselRef = useRef() const { scrollX } = useScroll({ container: carouselRef }) </script\> <template\> <div ref\="carouselRef" style\="overflow: scroll"\> <slot /> </div\> </template\> ### Element position We can track the progress of an element as it moves within a container by passing its `ref` to the `target` option. <script setup\> const ref = useRef() const { scrollYProgress } = useScroll({ target: ref, offset: \['start end', 'end end'\] }) </script\> <template\> <div ref\="ref"/> </template\> In this example, each item has its own progress indicator. ### Scroll offsets With the `offset` option we can define which parts of the element we want to track with the viewport, for instance track elements as they enter in from the bottom, leave at the top, or travel throughout the whole viewport. ## API `useScroll` accepts the following options. ### `container` **Default**: Browser window The scrollable container to track the scroll position of. By default, this is the window viewport. But it can be any scrollable element. ### `target` By default, this is the scrollable area of the container. It can additionally be set as another element, to track its progress within the viewport. ### `axis` **Default:** `"y"` The scroll axis to apply `offset`. ### `offset` **Default:** `["start start", "end end"]` `offset` describes intersections, points where the `target` and `container` meet. For example, the intersection `"start end"` means when the **start of the target** on the tracked axis meets the **end of the container.** So if the target is an element, the container is the window, and we're tracking the vertical axis then `"start end"` is where the **top of the element** meets **the bottom of the viewport**. #### Accepted intersections Both target and container points can be defined as: * **Number:** A value where `0` represents the start of the axis and `1` represents the end. So to define the top of the target with the middle of the container you could define `"0 0.5"`. Values outside this range are permitted. * **Names:** `"start"`, `"center"` and `"end"` can be used as clear shortcuts for `0`, `0.5` and `1` respectively. * **Pixels:** Pixel values like `"100px"`, `"-50px"` will be defined as that number of pixels from the start of the target/container. * **Percent:** Same as raw numbers but expressed as `"0%"` to `"100%"`. * **Viewport:** `"vh"` and `"vw"` units are accepted. `useScroll` is used to create scroll-linked animations, like progress indicators and parallax effects. <script setup\> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> ## Usage Import `useScroll` from Motion: import { useScroll } from "motion-v" `useScroll` returns four motion values: * `scrollX`/`Y`: The absolute scroll position, in pixels. * `scrollXProgress`/`YProgress`: The scroll position between the defined offsets, as a value between `0` and `1`. ### Page scroll By default, useScroll tracks the page scroll. const { scrollY } = useScroll() useMotionValueEvent(scrollY, "change", (latest) \=> { console.log("Page scroll: ", latest) }) For example, we could show a page scroll indicator by passing `scrollYProgress` straight to the `scaleX` style of a progress bar. <script setup\> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> As `useScroll` returns motion values, we can compose this scroll info with other motion value hooks like `useTransform` and `useSpring`: <script setup\> const { scrollYProgress } = useScroll() const scaleX = useSpring(scrollYProgress) </script\> <template\> <Motion :style\="{ scaleX }" /> </template\> > Since `scrollY` is a `MotionValue`, there's a neat trick you can use to tell when the user's scroll direction changes: > > <script setup\> > const { scrollY } = useScroll() > const scrollDirection = ref('down') > > useMotionValueEvent(scrollY, 'change', (current) \=> { > const diff = current - scrollY.getPrevious() > scrollDirection.value = diff > 0 ? 'down' : 'up' > }) > </script\> > > Perfect for triggering a sticky header animation! > > ~ Sam Selikoff, Motion for React Recipes ### Element scroll To track the scroll position of a scrollable element we can pass the element's `ref` to `useScroll`'s `container` option: <script setup\> const carouselRef = useRef() const { scrollX } = useScroll({ container: carouselRef }) </script\> <template\> <div ref\="carouselRef" style\="overflow: scroll"\> <slot /> </div\> </template\> ### Element position We can track the progress of an element as it moves within a container by passing its `ref` to the `target` option. <script setup\> const ref = useRef() const { scrollYProgress } = useScroll({ target: ref, offset: \['start end', 'end end'\] }) </script\> <template\> <div ref\="ref"/> </template\> In this example, each item has its own progress indicator. ### Scroll offsets With the `offset` option we can define which parts of the element we want to track with the viewport, for instance track elements as they enter in from the bottom, leave at the top, or travel throughout the whole viewport. ## API `useScroll` accepts the following options. ### `container` **Default**: Browser window The scrollable container to track the scroll position of. By default, this is the window viewport. But it can be any scrollable element. ### `target` By default, this is the scrollable area of the container. It can additionally be set as another element, to track its progress within the viewport. ### `axis` **Default:** `"y"` The scroll axis to apply `offset`. ### `offset` **Default:** `["start start", "end end"]` `offset` describes intersections, points where the `target` and `container` meet. For example, the intersection `"start end"` means when the **start of the target** on the tracked axis meets the **end of the container.** So if the target is an element, the container is the window, and we're tracking the vertical axis then `"start end"` is where the **top of the element** meets **the bottom of the viewport**. #### Accepted intersections Both target and container points can be defined as: * **Number:** A value where `0` represents the start of the axis and `1` represents the end. So to define the top of the target with the middle of the container you could define `"0 0.5"`. Values outside this range are permitted. * **Names:** `"start"`, `"center"` and `"end"` can be used as clear shortcuts for `0`, `0.5` and `1` respectively. * **Pixels:** Pixel values like `"100px"`, `"-50px"` will be defined as that number of pixels from the start of the target/container. * **Percent:** Same as raw numbers but expressed as `"0%"` to `"100%"`. * **Viewport:** `"vh"` and `"vw"` units are accepted. `useScroll` is used to create scroll-linked animations, like progress indicators and parallax effects. <script setup\> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> ## Usage Import `useScroll` from Motion: import { useScroll } from "motion-v" `useScroll` returns four motion values: * `scrollX`/`Y`: The absolute scroll position, in pixels. * `scrollXProgress`/`YProgress`: The scroll position between the defined offsets, as a value between `0` and `1`. ### Page scroll By default, useScroll tracks the page scroll. const { scrollY } = useScroll() useMotionValueEvent(scrollY, "change", (latest) \=> { console.log("Page scroll: ", latest) }) For example, we could show a page scroll indicator by passing `scrollYProgress` straight to the `scaleX` style of a progress bar. <script setup\> const { scrollYProgress } = useScroll() </script\> <template\> <motion.div :style\="{ scaleX: scrollYProgress }" /> </template\> As `useScroll` returns motion values, we can compose this scroll info with other motion value hooks like `useTransform` and `useSpring`: <script setup\> const { scrollYProgress } = useScroll() const scaleX = useSpring(scrollYProgress) </script\> <template\> <Motion :style\="{ scaleX }" /> </template\> > Since `scrollY` is a `MotionValue`, there's a neat trick you can use to tell when the user's scroll direction changes: > > <script setup\> > const { scrollY } = useScroll() > const scrollDirection = ref('down') > > useMotionValueEvent(scrollY, 'change', (current) \=> { > const diff = current - scrollY.getPrevious() > scrollDirection.value = diff > 0 ? 'down' : 'up' > }) > </script\> > > Perfect for triggering a sticky header animation! > > ~ Sam Selikoff, Motion for React Recipes ### Element scroll To track the scroll position of a scrollable element we can pass the element's `ref` to `useScroll`'s `container` option: <script setup\> const carouselRef = useRef() const { scrollX } = useScroll({ container: carouselRef }) </script\> <template\> <div ref\="carouselRef" style\="overflow: scroll"\> <slot /> </div\> </template\> ### Element position We can track the progress of an element as it moves within a container by passing its `ref` to the `target` option. <script setup\> const ref = useRef() const { scrollYProgress } = useScroll({ target: ref, offset: \['start end', 'end end'\] }) </script\> <template\> <div ref\="ref"/> </template\> In this example, each item has its own progress indicator. ### Scroll offsets With the `offset` option we can define which parts of the element we want to track with the viewport, for instance track elements as they enter in from the bottom, leave at the top, or travel throughout the whole viewport. ## API `useScroll` accepts the following options. ### `container` **Default**: Browser window The scrollable container to track the scroll position of. By default, this is the window viewport. But it can be any scrollable element. ### `target` By default, this is the scrollable area of the container. It can additionally be set as another element, to track its progress within the viewport. ### `axis` **Default:** `"y"` The scroll axis to apply `offset`. ### `offset` **Default:** `["start start", "end end"]` `offset` describes intersections, points where the `target` and `container` meet. For example, the intersection `"start end"` means when the **start of the target** on the tracked axis meets the **end of the container.** So if the target is an element, the container is the window, and we're tracking the vertical axis then `"start end"` is where the **top of the element** meets **the bottom of the viewport**. #### Accepted intersections Both target and container points can be defined as: * **Number:** A value where `0` represents the start of the axis and `1` represents the end. So to define the top of the target with the middle of the container you could define `"0 0.5"`. Values outside this range are permitted. * **Names:** `"start"`, `"center"` and `"end"` can be used as clear shortcuts for `0`, `0.5` and `1` respectively. * **Pixels:** Pixel values like `"100px"`, `"-50px"` will be defined as that number of pixels from the start of the target/container. * **Percent:** Same as raw numbers but expressed as `"0%"` to `"100%"`. * **Viewport:** `"vh"` and `"vw"` units are accepted. --- ## Page: https://motion.dev/docs/vue-use-time # useTime `useTime` returns a motion value that updates once per frame with the duration, in milliseconds, since it was first created. This is especially useful in generating perpetual animations. <script setup\> const time = useTime() const rotate = useTransform(time, \[0, 4000\], \[0, 360\], { clamp: false }) </script\> <template\> <motion.div :style\="{ rotate }" /> </template\> ## Usage Import from Motion: import { useTime } from "motion-v" When called, `useTime` will create a new motion value. This value will update every frame with the time since its creation. You can use this either directly or by composing with other motion value hooks. const time = useTime() const rotate = useTransform( time, \[0, 4000\], // For every 4 seconds... \[0, 360\], // ...rotate 360deg { clamp: false } ) `useTime` returns a motion value that updates once per frame with the duration, in milliseconds, since it was first created. This is especially useful in generating perpetual animations. <script setup\> const time = useTime() const rotate = useTransform(time, \[0, 4000\], \[0, 360\], { clamp: false }) </script\> <template\> <motion.div :style\="{ rotate }" /> </template\> ## Usage Import from Motion: import { useTime } from "motion-v" When called, `useTime` will create a new motion value. This value will update every frame with the time since its creation. You can use this either directly or by composing with other motion value hooks. const time = useTime() const rotate = useTransform( time, \[0, 4000\], // For every 4 seconds... \[0, 360\], // ...rotate 360deg { clamp: false } ) `useTime` returns a motion value that updates once per frame with the duration, in milliseconds, since it was first created. This is especially useful in generating perpetual animations. <script setup\> const time = useTime() const rotate = useTransform(time, \[0, 4000\], \[0, 360\], { clamp: false }) </script\> <template\> <motion.div :style\="{ rotate }" /> </template\> ## Usage Import from Motion: import { useTime } from "motion-v" When called, `useTime` will create a new motion value. This value will update every frame with the time since its creation. You can use this either directly or by composing with other motion value hooks. const time = useTime() const rotate = useTransform( time, \[0, 4000\], // For every 4 seconds... \[0, 360\], // ...rotate 360deg { clamp: false } ) --- ## Page: https://motion.dev/docs/vue-use-transform # useTransform `useTransform` creates a new motion value that transforms the output of one or more motion values. const x = useMotionValue(1) const y = useMotionValue(1) const z = useTransform(() \=> x.get() + y.get()) // z.get() === 2 ## Usage Import from Motion: import { useTransform } from "motion-v" `useTransform` can be used in two ways: with a transform function and via value maps: // Transform function useTransform(() \=> x.get() \* 2) // Value mapping useTransform(x, \[0, 100\], \["#f00", "00f"\]) ### Transform function A transform function is a normal function that returns a value. useTransform(() \=> x.get() \* 2) Any motion values read in this function via the `get()` method will be automatically subscribed to. When these motion values change, the function will be run again on the next animation frame to calculate a new value. const distance = 100 const time = useTime() const y = useTransform(() \=> Math.sin(time.get() / 1000) \* distance) ### Value mapping `useTransform` can also map a single motion value from one range of values to another. To illustrate, look at this `x` motion value: const x = useMotionValue(0) We can use `useTransform` to create a new motion value called `opacity`. const opacity = useTransform(x, input, output) By defining an `input` range and an `output` range, we can define relationships like "when `x` is `0`, `opacity` should be `1`. When `x` is `100` pixels either side, `opacity` should be `0`". const input = \[\-100, 0, 100\] const output = \[0, 1, 0\] Both ranges can be **any length** but must be the **same length** as each other. The input range must always be a series of increasing or decreasing numbers. The output range must be values all of the same type, but can be in any order. It can also be any value type that Motion can animate, like numbers, units, colors and other strings. const backgroundColor = useTransform( x, \[0, 100\], \["#f00", "#00f"\] ) By setting `clamp: false`, the ranges will map perpetually. For instance, in this example we're saying "for every `100px` scrolled, rotate another `360deg`": const { scrollY } = useScroll() const rotate = useTransform( scrollY, \[0, 100\], \[0, 360\], { clamp: false } ) ## Options With value mapping, we can set some additional options. ### `clamp` **Default:** `true` If `true`, will clamp output to within the provided range. If `false`, will carry on mapping even when the input falls outside the provided range. <script setup\> const y = useTransform(x, \[0, 1\], \[0, 2\]) const z = useTransform(x, \[0, 1\], \[0, 2\], { clamp: false }) onMounted(() \=> { x.set(2) console.log(y.get()) // 2, input clamped console.log(z.get()) // 4 }) </script\> ### `ease` An easing function, or array of easing functions, to ease the mixing between each value. These must be JavaScript functions. <script setup\> import { cubicBezier, circOut, useTransform } from 'motion-v' const y = useTransform(x, \[0, 1\], \[0, 2\], { ease: circOut }) const z = useTransform( x, \[0, 1\], \[0, 2\], { ease: cubicBezier(0.17, 0.67, 0.83, 0.67) } ) </script\> ### `mixer` A function to use to mix between each pair of output values. This function will be called with each pair of output values and must return a new function, that accepts a progress value between `0` and `1` and returns the mixed value. This can be used to inject more advanced mixers than Framer Motion's default, for instance Flubber for morphing SVG paths. `useTransform` creates a new motion value that transforms the output of one or more motion values. const x = useMotionValue(1) const y = useMotionValue(1) const z = useTransform(() \=> x.get() + y.get()) // z.get() === 2 ## Usage Import from Motion: import { useTransform } from "motion-v" `useTransform` can be used in two ways: with a transform function and via value maps: // Transform function useTransform(() \=> x.get() \* 2) // Value mapping useTransform(x, \[0, 100\], \["#f00", "00f"\]) ### Transform function A transform function is a normal function that returns a value. useTransform(() \=> x.get() \* 2) Any motion values read in this function via the `get()` method will be automatically subscribed to. When these motion values change, the function will be run again on the next animation frame to calculate a new value. const distance = 100 const time = useTime() const y = useTransform(() \=> Math.sin(time.get() / 1000) \* distance) ### Value mapping `useTransform` can also map a single motion value from one range of values to another. To illustrate, look at this `x` motion value: const x = useMotionValue(0) We can use `useTransform` to create a new motion value called `opacity`. const opacity = useTransform(x, input, output) By defining an `input` range and an `output` range, we can define relationships like "when `x` is `0`, `opacity` should be `1`. When `x` is `100` pixels either side, `opacity` should be `0`". const input = \[\-100, 0, 100\] const output = \[0, 1, 0\] Both ranges can be **any length** but must be the **same length** as each other. The input range must always be a series of increasing or decreasing numbers. The output range must be values all of the same type, but can be in any order. It can also be any value type that Motion can animate, like numbers, units, colors and other strings. const backgroundColor = useTransform( x, \[0, 100\], \["#f00", "#00f"\] ) By setting `clamp: false`, the ranges will map perpetually. For instance, in this example we're saying "for every `100px` scrolled, rotate another `360deg`": const { scrollY } = useScroll() const rotate = useTransform( scrollY, \[0, 100\], \[0, 360\], { clamp: false } ) ## Options With value mapping, we can set some additional options. ### `clamp` **Default:** `true` If `true`, will clamp output to within the provided range. If `false`, will carry on mapping even when the input falls outside the provided range. <script setup\> const y = useTransform(x, \[0, 1\], \[0, 2\]) const z = useTransform(x, \[0, 1\], \[0, 2\], { clamp: false }) onMounted(() \=> { x.set(2) console.log(y.get()) // 2, input clamped console.log(z.get()) // 4 }) </script\> ### `ease` An easing function, or array of easing functions, to ease the mixing between each value. These must be JavaScript functions. <script setup\> import { cubicBezier, circOut, useTransform } from 'motion-v' const y = useTransform(x, \[0, 1\], \[0, 2\], { ease: circOut }) const z = useTransform( x, \[0, 1\], \[0, 2\], { ease: cubicBezier(0.17, 0.67, 0.83, 0.67) } ) </script\> ### `mixer` A function to use to mix between each pair of output values. This function will be called with each pair of output values and must return a new function, that accepts a progress value between `0` and `1` and returns the mixed value. This can be used to inject more advanced mixers than Framer Motion's default, for instance Flubber for morphing SVG paths. `useTransform` creates a new motion value that transforms the output of one or more motion values. const x = useMotionValue(1) const y = useMotionValue(1) const z = useTransform(() \=> x.get() + y.get()) // z.get() === 2 ## Usage Import from Motion: import { useTransform } from "motion-v" `useTransform` can be used in two ways: with a transform function and via value maps: // Transform function useTransform(() \=> x.get() \* 2) // Value mapping useTransform(x, \[0, 100\], \["#f00", "00f"\]) ### Transform function A transform function is a normal function that returns a value. useTransform(() \=> x.get() \* 2) Any motion values read in this function via the `get()` method will be automatically subscribed to. When these motion values change, the function will be run again on the next animation frame to calculate a new value. const distance = 100 const time = useTime() const y = useTransform(() \=> Math.sin(time.get() / 1000) \* distance) ### Value mapping `useTransform` can also map a single motion value from one range of values to another. To illustrate, look at this `x` motion value: const x = useMotionValue(0) We can use `useTransform` to create a new motion value called `opacity`. const opacity = useTransform(x, input, output) By defining an `input` range and an `output` range, we can define relationships like "when `x` is `0`, `opacity` should be `1`. When `x` is `100` pixels either side, `opacity` should be `0`". const input = \[\-100, 0, 100\] const output = \[0, 1, 0\] Both ranges can be **any length** but must be the **same length** as each other. The input range must always be a series of increasing or decreasing numbers. The output range must be values all of the same type, but can be in any order. It can also be any value type that Motion can animate, like numbers, units, colors and other strings. const backgroundColor = useTransform( x, \[0, 100\], \["#f00", "#00f"\] ) By setting `clamp: false`, the ranges will map perpetually. For instance, in this example we're saying "for every `100px` scrolled, rotate another `360deg`": const { scrollY } = useScroll() const rotate = useTransform( scrollY, \[0, 100\], \[0, 360\], { clamp: false } ) ## Options With value mapping, we can set some additional options. ### `clamp` **Default:** `true` If `true`, will clamp output to within the provided range. If `false`, will carry on mapping even when the input falls outside the provided range. <script setup\> const y = useTransform(x, \[0, 1\], \[0, 2\]) const z = useTransform(x, \[0, 1\], \[0, 2\], { clamp: false }) onMounted(() \=> { x.set(2) console.log(y.get()) // 2, input clamped console.log(z.get()) // 4 }) </script\> ### `ease` An easing function, or array of easing functions, to ease the mixing between each value. These must be JavaScript functions. <script setup\> import { cubicBezier, circOut, useTransform } from 'motion-v' const y = useTransform(x, \[0, 1\], \[0, 2\], { ease: circOut }) const z = useTransform( x, \[0, 1\], \[0, 2\], { ease: cubicBezier(0.17, 0.67, 0.83, 0.67) } ) </script\> ### `mixer` A function to use to mix between each pair of output values. This function will be called with each pair of output values and must return a new function, that accepts a progress value between `0` and `1` and returns the mixed value. This can be used to inject more advanced mixers than Framer Motion's default, for instance Flubber for morphing SVG paths. --- ## Page: https://motion.dev/docs/vue-use-velocity # useVelocity `useVelocity` accepts a motion value and returns a new one that updates with the provided motion value's velocity. <script setup\> const x = useMotionValue(0) const xVelocity = useVelocity(x) const scale = useTransform( xVelocity, \[\-3000, 0, 3000\], \[2, 1, 2\], { clamp: false } ) </script\> <template\> <Motion drag\="x" :style\="{ x, scale }" /> </template\> ## Usage Import `useVelocity` from Motion: import { useVelocity } from "motion-v" Pass any numerical motion value to `useVelocity`. It'll return a new motion value that updates with the velocity of the original value. <script setup\> import { useMotionValue, useVelocity, useMotionValueEvent } from 'motion-v' const x = useMotionValue(0) const xVelocity = useVelocity(x) useMotionValueEvent(xVelocity, 'change', (latest) \=> { console.log('Velocity', latest) }) </script\> <template\> <Motion :style\="{ x }" /> </template\> Any numerical motion value will work. Even one returned from `useVelocity`. const x = useMotionValue(0) const xVelocity = useVelocity(x) const xAcceleration = useVelocity(xVelocity) `useVelocity` accepts a motion value and returns a new one that updates with the provided motion value's velocity. <script setup\> const x = useMotionValue(0) const xVelocity = useVelocity(x) const scale = useTransform( xVelocity, \[\-3000, 0, 3000\], \[2, 1, 2\], { clamp: false } ) </script\> <template\> <Motion drag\="x" :style\="{ x, scale }" /> </template\> ## Usage Import `useVelocity` from Motion: import { useVelocity } from "motion-v" Pass any numerical motion value to `useVelocity`. It'll return a new motion value that updates with the velocity of the original value. <script setup\> import { useMotionValue, useVelocity, useMotionValueEvent } from 'motion-v' const x = useMotionValue(0) const xVelocity = useVelocity(x) useMotionValueEvent(xVelocity, 'change', (latest) \=> { console.log('Velocity', latest) }) </script\> <template\> <Motion :style\="{ x }" /> </template\> Any numerical motion value will work. Even one returned from `useVelocity`. const x = useMotionValue(0) const xVelocity = useVelocity(x) const xAcceleration = useVelocity(xVelocity) `useVelocity` accepts a motion value and returns a new one that updates with the provided motion value's velocity. <script setup\> const x = useMotionValue(0) const xVelocity = useVelocity(x) const scale = useTransform( xVelocity, \[\-3000, 0, 3000\], \[2, 1, 2\], { clamp: false } ) </script\> <template\> <Motion drag\="x" :style\="{ x, scale }" /> </template\> ## Usage Import `useVelocity` from Motion: import { useVelocity } from "motion-v" Pass any numerical motion value to `useVelocity`. It'll return a new motion value that updates with the velocity of the original value. <script setup\> import { useMotionValue, useVelocity, useMotionValueEvent } from 'motion-v' const x = useMotionValue(0) const xVelocity = useVelocity(x) useMotionValueEvent(xVelocity, 'change', (latest) \=> { console.log('Velocity', latest) }) </script\> <template\> <Motion :style\="{ x }" /> </template\> Any numerical motion value will work. Even one returned from `useVelocity`. const x = useMotionValue(0) const xVelocity = useVelocity(x) const xAcceleration = useVelocity(xVelocity) --- ## Page: https://motion.dev/docs/vue-use-animate # useAnimate `useAnimate` provides a way of using the `animate` function that is scoped to the elements within your component. This allows you to use manual animation controls, timelines, selectors scoped to your component, and automatic cleanup. It provides a `scope` ref, and an `animate` function where every DOM selector is scoped to this ref. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() onMounted(() \=> { // This "li" selector will only select children // of the element that receives \`scope\` animate('li', { opacity: 1 }) }) </script\> <template\> <ul ref\="scope"\> <slot /> </ul\> </template\> Additionally, when the component calling `useAnimate` is removed, all animations started with its `animate` function will be cleaned up automatically. ## Usage Import from Motion: import { useAnimate } from "motion-v" `useAnimate` returns two arguments, a `scope` ref and an `animate` function. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() </script\> This `scope` ref must be passed to either a regular HTML/SVG element or a `motion` component. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() </script\> <template\> <ul ref\="scope"\> <slot /> </ul\> </template\> This scoped `animate` function can now be used in effects and event handlers to animate elements. We can either use the scoped element directly: animate(scope.current, { opacity: 1 }, { duration: 1 }) Or by passing it a selector: animate("li", { backgroundColor: "#000" }, { ease: "linear" }) This selector is `"li"`, but we're not selecting all `li` elements on the page, only those that are a child of the scoped element. ### Scroll-triggered animations Animations can be triggered when the scope scrolls into view by combining `useAnimate` with `useInView`. <script setup\> import { useAnimate, useInView } from 'motion-v' const \[scope, animate\] = useAnimate() const isInView = useInView(scope) watch(isInView, (inView) \=> { if (inView) { animate(scope.value, { opacity: 1 }) } }) </script\> <template\> <ul ref\="scope"\> <li /> <li /> <li /> </ul\> </template\> `useAnimate` provides a way of using the `animate` function that is scoped to the elements within your component. This allows you to use manual animation controls, timelines, selectors scoped to your component, and automatic cleanup. It provides a `scope` ref, and an `animate` function where every DOM selector is scoped to this ref. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() onMounted(() \=> { // This "li" selector will only select children // of the element that receives \`scope\` animate('li', { opacity: 1 }) }) </script\> <template\> <ul ref\="scope"\> <slot /> </ul\> </template\> Additionally, when the component calling `useAnimate` is removed, all animations started with its `animate` function will be cleaned up automatically. ## Usage Import from Motion: import { useAnimate } from "motion-v" `useAnimate` returns two arguments, a `scope` ref and an `animate` function. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() </script\> This `scope` ref must be passed to either a regular HTML/SVG element or a `motion` component. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() </script\> <template\> <ul ref\="scope"\> <slot /> </ul\> </template\> This scoped `animate` function can now be used in effects and event handlers to animate elements. We can either use the scoped element directly: animate(scope.current, { opacity: 1 }, { duration: 1 }) Or by passing it a selector: animate("li", { backgroundColor: "#000" }, { ease: "linear" }) This selector is `"li"`, but we're not selecting all `li` elements on the page, only those that are a child of the scoped element. ### Scroll-triggered animations Animations can be triggered when the scope scrolls into view by combining `useAnimate` with `useInView`. <script setup\> import { useAnimate, useInView } from 'motion-v' const \[scope, animate\] = useAnimate() const isInView = useInView(scope) watch(isInView, (inView) \=> { if (inView) { animate(scope.value, { opacity: 1 }) } }) </script\> <template\> <ul ref\="scope"\> <li /> <li /> <li /> </ul\> </template\> `useAnimate` provides a way of using the `animate` function that is scoped to the elements within your component. This allows you to use manual animation controls, timelines, selectors scoped to your component, and automatic cleanup. It provides a `scope` ref, and an `animate` function where every DOM selector is scoped to this ref. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() onMounted(() \=> { // This "li" selector will only select children // of the element that receives \`scope\` animate('li', { opacity: 1 }) }) </script\> <template\> <ul ref\="scope"\> <slot /> </ul\> </template\> Additionally, when the component calling `useAnimate` is removed, all animations started with its `animate` function will be cleaned up automatically. ## Usage Import from Motion: import { useAnimate } from "motion-v" `useAnimate` returns two arguments, a `scope` ref and an `animate` function. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() </script\> This `scope` ref must be passed to either a regular HTML/SVG element or a `motion` component. <script setup\> import { useAnimate } from 'motion-v' const \[scope, animate\] = useAnimate() </script\> <template\> <ul ref\="scope"\> <slot /> </ul\> </template\> This scoped `animate` function can now be used in effects and event handlers to animate elements. We can either use the scoped element directly: animate(scope.current, { opacity: 1 }, { duration: 1 }) Or by passing it a selector: animate("li", { backgroundColor: "#000" }, { ease: "linear" }) This selector is `"li"`, but we're not selecting all `li` elements on the page, only those that are a child of the scoped element. ### Scroll-triggered animations Animations can be triggered when the scope scrolls into view by combining `useAnimate` with `useInView`. <script setup\> import { useAnimate, useInView } from 'motion-v' const \[scope, animate\] = useAnimate() const isInView = useInView(scope) watch(isInView, (inView) \=> { if (inView) { animate(scope.value, { opacity: 1 }) } }) </script\> <template\> <ul ref\="scope"\> <li /> <li /> <li /> </ul\> </template\> --- ## Page: https://motion.dev/docs/vue-use-animation-frame # useAnimationFrame `useAnimationFrame` runs a callback once every animation frame. useAnimationFrame((time) \=> { domRef.value.style.transform = \`rotateY(${time}deg)\` }) The callback is provided two arguments: * `time`, the total duration of time since the callback was first called. * `delta`, the total duration of time since the last animation frame. <script setup\> import { useAnimationFrame } from 'motion-v' const domRef = ref() useAnimationFrame((time, delta) \=> { domRef.value.style.transform = \`rotateY(${time}deg)\` }) </script\> <template\> <div :ref\="domRef" /> </template\> `useAnimationFrame` runs a callback once every animation frame. useAnimationFrame((time) \=> { domRef.value.style.transform = \`rotateY(${time}deg)\` }) The callback is provided two arguments: * `time`, the total duration of time since the callback was first called. * `delta`, the total duration of time since the last animation frame. <script setup\> import { useAnimationFrame } from 'motion-v' const domRef = ref() useAnimationFrame((time, delta) \=> { domRef.value.style.transform = \`rotateY(${time}deg)\` }) </script\> <template\> <div :ref\="domRef" /> </template\> `useAnimationFrame` runs a callback once every animation frame. useAnimationFrame((time) \=> { domRef.value.style.transform = \`rotateY(${time}deg)\` }) The callback is provided two arguments: * `time`, the total duration of time since the callback was first called. * `delta`, the total duration of time since the last animation frame. <script setup\> import { useAnimationFrame } from 'motion-v' const domRef = ref() useAnimationFrame((time, delta) \=> { domRef.value.style.transform = \`rotateY(${time}deg)\` }) </script\> <template\> <div :ref\="domRef" /> </template\> --- ## Page: https://motion.dev/docs/vue-use-drag-controls # useDragControls Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop and then moving the pointer. For some use-cases, for example clicking at an arbitrary point on a video scrubber, we might want to initiate that dragging from a different element. With `useDragControls`, we can create a set of controls to manually start dragging from any pointer event. ## Usage Import `useDragControls` from Motion: import { useDragControls } from "motion-v" `useDragControls` returns drag controls that can be passed to a draggable `motion` component: <script setup\> import { useDragControls, motion } from 'motion-v' const controls = useDragControls() </script\> <template\> <motion.div drag :drag\-controls\="controls" /> </template\> Now we can start a drag session from another any element's `onPointerDown` event via the `start` method. <template\> <div @pointerdown\="(event) \=> controls.start(event)" /> </template\> ### Touch support To support touch screens, the triggering element should have the `touch-action: none` style applied. <template\> <div @pointerdown\="startDrag" style\="touch-action: none" /> </template\> ### Snap to cursor By default, the drag gesture will only apply **changes** to the pointer position. We can also make the `motion` component immediately snap to the cursor by passing `snapToCursor: true` to the `start` method. controls.start(event, { snapToCursor: true }) ### Disable automatic drag With this configuration, the `motion` component will still automatically start a drag gesture when it receives a `pointerdown` event itself. We can stop this behaviour by passing it a `:dragListener="false"` prop. <template\> <motion.div drag :drag-listener\="false" :drag-controls="controls" /> </template\> Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop and then moving the pointer. For some use-cases, for example clicking at an arbitrary point on a video scrubber, we might want to initiate that dragging from a different element. With `useDragControls`, we can create a set of controls to manually start dragging from any pointer event. ## Usage Import `useDragControls` from Motion: import { useDragControls } from "motion-v" `useDragControls` returns drag controls that can be passed to a draggable `motion` component: <script setup\> import { useDragControls, motion } from 'motion-v' const controls = useDragControls() </script\> <template\> <motion.div drag :drag\-controls\="controls" /> </template\> Now we can start a drag session from another any element's `onPointerDown` event via the `start` method. <template\> <div @pointerdown\="(event) \=> controls.start(event)" /> </template\> ### Touch support To support touch screens, the triggering element should have the `touch-action: none` style applied. <template\> <div @pointerdown\="startDrag" style\="touch-action: none" /> </template\> ### Snap to cursor By default, the drag gesture will only apply **changes** to the pointer position. We can also make the `motion` component immediately snap to the cursor by passing `snapToCursor: true` to the `start` method. controls.start(event, { snapToCursor: true }) ### Disable automatic drag With this configuration, the `motion` component will still automatically start a drag gesture when it receives a `pointerdown` event itself. We can stop this behaviour by passing it a `:dragListener="false"` prop. <template\> <motion.div drag :drag-listener\="false" :drag-controls="controls" /> </template\> Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop and then moving the pointer. For some use-cases, for example clicking at an arbitrary point on a video scrubber, we might want to initiate that dragging from a different element. With `useDragControls`, we can create a set of controls to manually start dragging from any pointer event. ## Usage Import `useDragControls` from Motion: import { useDragControls } from "motion-v" `useDragControls` returns drag controls that can be passed to a draggable `motion` component: <script setup\> import { useDragControls, motion } from 'motion-v' const controls = useDragControls() </script\> <template\> <motion.div drag :drag\-controls\="controls" /> </template\> Now we can start a drag session from another any element's `onPointerDown` event via the `start` method. <template\> <div @pointerdown\="(event) \=> controls.start(event)" /> </template\> ### Touch support To support touch screens, the triggering element should have the `touch-action: none` style applied. <template\> <div @pointerdown\="startDrag" style\="touch-action: none" /> </template\> ### Snap to cursor By default, the drag gesture will only apply **changes** to the pointer position. We can also make the `motion` component immediately snap to the cursor by passing `snapToCursor: true` to the `start` method. controls.start(event, { snapToCursor: true }) ### Disable automatic drag With this configuration, the `motion` component will still automatically start a drag gesture when it receives a `pointerdown` event itself. We can stop this behaviour by passing it a `:dragListener="false"` prop. <template\> <motion.div drag :drag-listener\="false" :drag-controls="controls" /> </template\> --- ## Page: https://motion.dev/docs/vue-use-in-view # useInView `useInView` is a tiny (0.6kb) hook that detects when the provided element is within the viewport. It can be used with any Vue element. <script setup\> import { useInView } from 'motion-v' const domRef = ref() const isInView = useInView(domRef) </script\> <template\> <div ref\="domRef" /> </template\> ## Usage Import `useInView` from Motion: import { useInView } from "motion-v" `useInView` can track the visibility of any HTML element. Pass a `ref` object to both `useInView` and the HTML element. <script setup\> import { useInView } from 'motion-v' const domRef = ref() const isInView = useInView(ref) </script\> <template\> <div ref\="domRef" /> </template\> While the element is outside the viewport, `useInView` will return `false`. When it moves inside the view, it'll re-render the component and return `true`. ### Effects `useInView` is vanilla Vue ref state, so firing functions when `isInView` changes is a matter of passing it to a `watch`. <script setup\> watch(isInView, (inView) \=> { console.log('Element is in view: ', inView) }) </script\> ## Options `useInView` can accept options to define how the element is tracked within the viewport. const isInView = useInView(domRef, { once: true }) ### `root` By default, `useInView` will track the visibility of an element as it enters/leaves the window viewport. Set `root` to be the ref of a scrollable parent, and it'll use that element to be the viewport instead. <script setup\> import { useInView } from 'motion-v' import { computed } from 'vue' const container = ref() const boxRef = ref() const isInView = useInView(boxRef,computed(() \=> ({ root: container.value }))) </script\> <template\> <div ref\="container" style\="overflow: scroll"\> <div ref\="boxRef" /> </div\> </template\> ### `margin` **Default:** `"0px"` A margin to add to the viewport to change the detection area. Use multiple values to adjust top/right/bottom/left, e.g. `"0px -20px 0px 100px"`. const isInView = useInView({ margin: "0px 100px -50px 0px" }) **\]Note:** For browser security reasons, `margin` won't take affect within cross-origin iframes unless `root` is explicitly defined. ### `once` **Default:** `false` If `true`, once an element is in view, useInView will stop observing the element and always return `true`. const isInView = useInView(ref, { once: true }) ### `initial` **Default:** `false` Set an initial value to return until the element has been measured. const isInView = useInView(ref, { initial: true }) ### `amount` **Default:** `"some"` The amount of an element that should enter the viewport to be considered "entered". Either `"some"`, `"all"` or a number between `0` and `1`. ## Example `useInView` is a tiny (0.6kb) hook that detects when the provided element is within the viewport. It can be used with any Vue element. <script setup\> import { useInView } from 'motion-v' const domRef = ref() const isInView = useInView(domRef) </script\> <template\> <div ref\="domRef" /> </template\> ## Usage Import `useInView` from Motion: import { useInView } from "motion-v" `useInView` can track the visibility of any HTML element. Pass a `ref` object to both `useInView` and the HTML element. <script setup\> import { useInView } from 'motion-v' const domRef = ref() const isInView = useInView(ref) </script\> <template\> <div ref\="domRef" /> </template\> While the element is outside the viewport, `useInView` will return `false`. When it moves inside the view, it'll re-render the component and return `true`. ### Effects `useInView` is vanilla Vue ref state, so firing functions when `isInView` changes is a matter of passing it to a `watch`. <script setup\> watch(isInView, (inView) \=> { console.log('Element is in view: ', inView) }) </script\> ## Options `useInView` can accept options to define how the element is tracked within the viewport. const isInView = useInView(domRef, { once: true }) ### `root` By default, `useInView` will track the visibility of an element as it enters/leaves the window viewport. Set `root` to be the ref of a scrollable parent, and it'll use that element to be the viewport instead. <script setup\> import { useInView } from 'motion-v' import { computed } from 'vue' const container = ref() const boxRef = ref() const isInView = useInView(boxRef,computed(() \=> ({ root: container.value }))) </script\> <template\> <div ref\="container" style\="overflow: scroll"\> <div ref\="boxRef" /> </div\> </template\> ### `margin` **Default:** `"0px"` A margin to add to the viewport to change the detection area. Use multiple values to adjust top/right/bottom/left, e.g. `"0px -20px 0px 100px"`. const isInView = useInView({ margin: "0px 100px -50px 0px" }) **\]Note:** For browser security reasons, `margin` won't take affect within cross-origin iframes unless `root` is explicitly defined. ### `once` **Default:** `false` If `true`, once an element is in view, useInView will stop observing the element and always return `true`. const isInView = useInView(ref, { once: true }) ### `initial` **Default:** `false` Set an initial value to return until the element has been measured. const isInView = useInView(ref, { initial: true }) ### `amount` **Default:** `"some"` The amount of an element that should enter the viewport to be considered "entered". Either `"some"`, `"all"` or a number between `0` and `1`. ## Example `useInView` is a tiny (0.6kb) hook that detects when the provided element is within the viewport. It can be used with any Vue element. <script setup\> import { useInView } from 'motion-v' const domRef = ref() const isInView = useInView(domRef) </script\> <template\> <div ref\="domRef" /> </template\> ## Usage Import `useInView` from Motion: import { useInView } from "motion-v" `useInView` can track the visibility of any HTML element. Pass a `ref` object to both `useInView` and the HTML element. <script setup\> import { useInView } from 'motion-v' const domRef = ref() const isInView = useInView(ref) </script\> <template\> <div ref\="domRef" /> </template\> While the element is outside the viewport, `useInView` will return `false`. When it moves inside the view, it'll re-render the component and return `true`. ### Effects `useInView` is vanilla Vue ref state, so firing functions when `isInView` changes is a matter of passing it to a `watch`. <script setup\> watch(isInView, (inView) \=> { console.log('Element is in view: ', inView) }) </script\> ## Options `useInView` can accept options to define how the element is tracked within the viewport. const isInView = useInView(domRef, { once: true }) ### `root` By default, `useInView` will track the visibility of an element as it enters/leaves the window viewport. Set `root` to be the ref of a scrollable parent, and it'll use that element to be the viewport instead. <script setup\> import { useInView } from 'motion-v' import { computed } from 'vue' const container = ref() const boxRef = ref() const isInView = useInView(boxRef,computed(() \=> ({ root: container.value }))) </script\> <template\> <div ref\="container" style\="overflow: scroll"\> <div ref\="boxRef" /> </div\> </template\> ### `margin` **Default:** `"0px"` A margin to add to the viewport to change the detection area. Use multiple values to adjust top/right/bottom/left, e.g. `"0px -20px 0px 100px"`. const isInView = useInView({ margin: "0px 100px -50px 0px" }) **\]Note:** For browser security reasons, `margin` won't take affect within cross-origin iframes unless `root` is explicitly defined. ### `once` **Default:** `false` If `true`, once an element is in view, useInView will stop observing the element and always return `true`. const isInView = useInView(ref, { once: true }) ### `initial` **Default:** `false` Set an initial value to return until the element has been measured. const isInView = useInView(ref, { initial: true }) ### `amount` **Default:** `"some"` The amount of an element that should enter the viewport to be considered "entered". Either `"some"`, `"all"` or a number between `0` and `1`. ## Example --- ## Page: https://motion.dev/docs/vue-use-reduced-motion # useReducedMotion A hook that returns `true` if the current device has Reduced Motion setting enabled. const shouldReduceMotion = useReducedMotion() This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing potentially motion-sickness inducing `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. It will actively respond to changes and re-render your components with the latest setting. <script setup\> import { useReducedMotion } from 'motion-v' const props = defineProps({ isOpen: Boolean }) const shouldReduceMotion = useReducedMotion() const closedX = computed(()\=>shouldReduceMotion.value ? 0 : '-100%') </script\> <template\> <Motion :animate\="{ opacity: isOpen ? 1 : 0, x: isOpen ? 0 : closedX }" /> </template\> ## Usage Import `useReducedMotion` from Motion: import { useReducedMotion } from "motion-v" In any component, call `useReducedMotion` to check whether the device's Reduced Motion setting is enabled. const prefersReducedMotion = useReducedMotion() You can then use this `true`/`false` value to change your application logic. A hook that returns `true` if the current device has Reduced Motion setting enabled. const shouldReduceMotion = useReducedMotion() This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing potentially motion-sickness inducing `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. It will actively respond to changes and re-render your components with the latest setting. <script setup\> import { useReducedMotion } from 'motion-v' const props = defineProps({ isOpen: Boolean }) const shouldReduceMotion = useReducedMotion() const closedX = computed(()\=>shouldReduceMotion.value ? 0 : '-100%') </script\> <template\> <Motion :animate\="{ opacity: isOpen ? 1 : 0, x: isOpen ? 0 : closedX }" /> </template\> ## Usage Import `useReducedMotion` from Motion: import { useReducedMotion } from "motion-v" In any component, call `useReducedMotion` to check whether the device's Reduced Motion setting is enabled. const prefersReducedMotion = useReducedMotion() You can then use this `true`/`false` value to change your application logic. A hook that returns `true` if the current device has Reduced Motion setting enabled. const shouldReduceMotion = useReducedMotion() This can be used to implement changes to your UI based on Reduced Motion. For instance, replacing potentially motion-sickness inducing `x`/`y` animations with `opacity`, disabling the autoplay of background videos, or turning off parallax motion. It will actively respond to changes and re-render your components with the latest setting. <script setup\> import { useReducedMotion } from 'motion-v' const props = defineProps({ isOpen: Boolean }) const shouldReduceMotion = useReducedMotion() const closedX = computed(()\=>shouldReduceMotion.value ? 0 : '-100%') </script\> <template\> <Motion :animate\="{ opacity: isOpen ? 1 : 0, x: isOpen ? 0 : closedX }" /> </template\> ## Usage Import `useReducedMotion` from Motion: import { useReducedMotion } from "motion-v" In any component, call `useReducedMotion` to check whether the device's Reduced Motion setting is enabled. const prefersReducedMotion = useReducedMotion() You can then use this `true`/`false` value to change your application logic. --- ## Page: https://motion.dev/docs/vue-radix # Integrate Motion with Radix Radix-Vue is one of the most popular component libraries for Vue, and it takes just a couple steps to use Motion for Vue for animations. In this guide, we'll learn how to use `motion` components with Radix primitives, as well as specific setups for exit and layout animations. ## Setup `motion` components Most Radix components render and control their own DOM elements. But they also provide the `asChild` prop that, when set to `true`, will make the component use the first provided child as its DOM node instead. By passing a `motion` component as this child, we can now use all of its animation props as normal: <template\> <ToastRoot :as\-child\="true"\> <Motion :initial\="{ opacity: 0 }" :animate\="{ opacity: 1 }" layout \> ## Exit animations Many Radix components, like Toast or Tooltip, would be perfect for exit animations, but can't perform them without Motion's `AnimatePresence`. `AnimatePresence` is built on top of Vue's Transition component,This is how it tracks which components are exiting: <template\> <AnimatePresence\> <Motion v-if\="isOpen" :exit="{ opacity: 0 }" /> </AnimatePresence\> </template\> Using exit animations with Motion Vue and Radix components is straightforward. Just wrap your unmounting component with `AnimatePresence`, and it can detect the direct child DOM unmounting and trigger exit animations. For example, works with the Tooltip component: <Tooltip.Provider\> <Tooltip.Root\> <Tooltip.Trigger class\="tooltip-trigger"\> Hover or focus </Tooltip.Trigger\> <Tooltip.Portal\> <AnimatePresence\> <Tooltip.Content as-child :side\-offset\="10"\> <motion.div class\="tooltip-content" :initial\="{ opacity: 0, y: 20, scale: 0.8 }" :animate\="{ opacity: 1, y: 0, scale: 1 }" :exit\="{ opacity: 0,y: 20,}" \> Add to library <Tooltip.Arrow class\="tooltip-arrow" /> </motion.div\> </Tooltip.Content\> </AnimatePresence\> </Tooltip.Portal\> </Tooltip.Root\> </Tooltip.Provider\> ## Layout animations Layout animations also require this same pattern of hoisting state out of the component. <script setup\> const tab = ref('account') </script\> <template\> <Tabs.Root v-model\="tab" as-child \> <motion.div layout\> This is to ensure `motion` components know to perform layout animations when the state changes. You can even pass this state to `layoutDependency` for better performance. <motion.div layout :layoutDependency\="tab"\> ## Motion+ examples Motion+ is a one-time payment, lifetime membership that gains you access to the source code of an ever-growing library of premium examples, as well as premium components like `Cursor` and `AnimateNumber`. Motion+ features examples for most Radix components: * Accordion * Checkbox * Context Menu * Dialog * Dropdown Menu * Progress * Radio Group * Select * Slider * Switch * Tabs * Toast * Toggle Group * Toolbar * Tooltip Radix-Vue is one of the most popular component libraries for Vue, and it takes just a couple steps to use Motion for Vue for animations. In this guide, we'll learn how to use `motion` components with Radix primitives, as well as specific setups for exit and layout animations. ## Setup `motion` components Most Radix components render and control their own DOM elements. But they also provide the `asChild` prop that, when set to `true`, will make the component use the first provided child as its DOM node instead. By passing a `motion` component as this child, we can now use all of its animation props as normal: <template\> <ToastRoot :as\-child\="true"\> <Motion :initial\="{ opacity: 0 }" :animate\="{ opacity: 1 }" layout \> ## Exit animations Many Radix components, like Toast or Tooltip, would be perfect for exit animations, but can't perform them without Motion's `AnimatePresence`. `AnimatePresence` is built on top of Vue's Transition component,This is how it tracks which components are exiting: <template\> <AnimatePresence\> <Motion v-if\="isOpen" :exit="{ opacity: 0 }" /> </AnimatePresence\> </template\> Using exit animations with Motion Vue and Radix components is straightforward. Just wrap your unmounting component with `AnimatePresence`, and it can detect the direct child DOM unmounting and trigger exit animations. For example, works with the Tooltip component: <Tooltip.Provider\> <Tooltip.Root\> <Tooltip.Trigger class\="tooltip-trigger"\> Hover or focus </Tooltip.Trigger\> <Tooltip.Portal\> <AnimatePresence\> <Tooltip.Content as-child :side\-offset\="10"\> <motion.div class\="tooltip-content" :initial\="{ opacity: 0, y: 20, scale: 0.8 }" :animate\="{ opacity: 1, y: 0, scale: 1 }" :exit\="{ opacity: 0,y: 20,}" \> Add to library <Tooltip.Arrow class\="tooltip-arrow" /> </motion.div\> </Tooltip.Content\> </AnimatePresence\> </Tooltip.Portal\> </Tooltip.Root\> </Tooltip.Provider\> ## Layout animations Layout animations also require this same pattern of hoisting state out of the component. <script setup\> const tab = ref('account') </script\> <template\> <Tabs.Root v-model\="tab" as-child \> <motion.div layout\> This is to ensure `motion` components know to perform layout animations when the state changes. You can even pass this state to `layoutDependency` for better performance. <motion.div layout :layoutDependency\="tab"\> ## Motion+ examples Motion+ is a one-time payment, lifetime membership that gains you access to the source code of an ever-growing library of premium examples, as well as premium components like `Cursor` and `AnimateNumber`. Motion+ features examples for most Radix components: * Accordion * Checkbox * Context Menu * Dialog * Dropdown Menu * Progress * Radio Group * Select * Slider * Switch * Tabs * Toast * Toggle Group * Toolbar * Tooltip Radix-Vue is one of the most popular component libraries for Vue, and it takes just a couple steps to use Motion for Vue for animations. In this guide, we'll learn how to use `motion` components with Radix primitives, as well as specific setups for exit and layout animations. ## Setup `motion` components Most Radix components render and control their own DOM elements. But they also provide the `asChild` prop that, when set to `true`, will make the component use the first provided child as its DOM node instead. By passing a `motion` component as this child, we can now use all of its animation props as normal: <template\> <ToastRoot :as\-child\="true"\> <Motion :initial\="{ opacity: 0 }" :animate\="{ opacity: 1 }" layout \> ## Exit animations Many Radix components, like Toast or Tooltip, would be perfect for exit animations, but can't perform them without Motion's `AnimatePresence`. `AnimatePresence` is built on top of Vue's Transition component,This is how it tracks which components are exiting: <template\> <AnimatePresence\> <Motion v-if\="isOpen" :exit="{ opacity: 0 }" /> </AnimatePresence\> </template\> Using exit animations with Motion Vue and Radix components is straightforward. Just wrap your unmounting component with `AnimatePresence`, and it can detect the direct child DOM unmounting and trigger exit animations. For example, works with the Tooltip component: <Tooltip.Provider\> <Tooltip.Root\> <Tooltip.Trigger class\="tooltip-trigger"\> Hover or focus </Tooltip.Trigger\> <Tooltip.Portal\> <AnimatePresence\> <Tooltip.Content as-child :side\-offset\="10"\> <motion.div class\="tooltip-content" :initial\="{ opacity: 0, y: 20, scale: 0.8 }" :animate\="{ opacity: 1, y: 0, scale: 1 }" :exit\="{ opacity: 0,y: 20,}" \> Add to library <Tooltip.Arrow class\="tooltip-arrow" /> </motion.div\> </Tooltip.Content\> </AnimatePresence\> </Tooltip.Portal\> </Tooltip.Root\> </Tooltip.Provider\> ## Layout animations Layout animations also require this same pattern of hoisting state out of the component. <script setup\> const tab = ref('account') </script\> <template\> <Tabs.Root v-model\="tab" as-child \> <motion.div layout\> This is to ensure `motion` components know to perform layout animations when the state changes. You can even pass this state to `layoutDependency` for better performance. <motion.div layout :layoutDependency\="tab"\> ## Motion+ examples Motion+ is a one-time payment, lifetime membership that gains you access to the source code of an ever-growing library of premium examples, as well as premium components like `Cursor` and `AnimateNumber`. Motion+ features examples for most Radix components: * Accordion * Checkbox * Context Menu * Dialog * Dropdown Menu * Progress * Radio Group * Select * Slider * Switch * Tabs * Toast * Toggle Group * Toolbar * Tooltip