@pythoughts/vue-skills-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/index.mjs +139 -0
- package/package.json +34 -0
- package/skills/create-adaptable-composable/SKILL.md +76 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +350 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +346 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-debug-guides/SKILL.md +205 -0
- package/skills/vue-debug-guides/reference/animation-key-for-rerender.md +160 -0
- package/skills/vue-debug-guides/reference/animation-transitiongroup-performance.md +241 -0
- package/skills/vue-debug-guides/reference/async-component-error-handling.md +115 -0
- package/skills/vue-debug-guides/reference/async-component-keepalive-ref-issue.md +112 -0
- package/skills/vue-debug-guides/reference/async-component-suspense-control.md +84 -0
- package/skills/vue-debug-guides/reference/async-component-vue-router.md +109 -0
- package/skills/vue-debug-guides/reference/attrs-event-listener-merging.md +205 -0
- package/skills/vue-debug-guides/reference/checkbox-true-false-value-form-submission.md +118 -0
- package/skills/vue-debug-guides/reference/cleanup-side-effects.md +172 -0
- package/skills/vue-debug-guides/reference/click-events-on-components.md +180 -0
- package/skills/vue-debug-guides/reference/component-naming-conflicts.md +159 -0
- package/skills/vue-debug-guides/reference/component-ref-requires-defineexpose.md +176 -0
- package/skills/vue-debug-guides/reference/composable-avoid-hidden-side-effects.md +208 -0
- package/skills/vue-debug-guides/reference/composable-call-location-restrictions.md +141 -0
- package/skills/vue-debug-guides/reference/composable-naming-return-pattern.md +139 -0
- package/skills/vue-debug-guides/reference/composable-tovalue-inside-watcheffect.md +182 -0
- package/skills/vue-debug-guides/reference/composition-api-not-functional-programming.md +120 -0
- package/skills/vue-debug-guides/reference/composition-api-script-setup-async-context.md +203 -0
- package/skills/vue-debug-guides/reference/composition-api-vs-react-hooks-differences.md +156 -0
- package/skills/vue-debug-guides/reference/computed-array-mutation.md +148 -0
- package/skills/vue-debug-guides/reference/computed-conditional-dependencies.md +147 -0
- package/skills/vue-debug-guides/reference/computed-no-parameters.md +159 -0
- package/skills/vue-debug-guides/reference/computed-no-side-effects.md +107 -0
- package/skills/vue-debug-guides/reference/computed-return-value-readonly.md +160 -0
- package/skills/vue-debug-guides/reference/configure-app-before-mount.md +89 -0
- package/skills/vue-debug-guides/reference/declare-emits-for-documentation.md +212 -0
- package/skills/vue-debug-guides/reference/define-expose-before-await.md +192 -0
- package/skills/vue-debug-guides/reference/define-model-default-value-sync.md +139 -0
- package/skills/vue-debug-guides/reference/defineEmits-must-be-top-level.md +164 -0
- package/skills/vue-debug-guides/reference/defineEmits-no-runtime-and-type-mixed.md +170 -0
- package/skills/vue-debug-guides/reference/definemodel-object-mutation-no-emit.md +148 -0
- package/skills/vue-debug-guides/reference/dom-update-timing-nexttick.md +90 -0
- package/skills/vue-debug-guides/reference/dynamic-argument-constraints.md +146 -0
- package/skills/vue-debug-guides/reference/dynamic-component-registration-vite.md +147 -0
- package/skills/vue-debug-guides/reference/event-modifier-order-matters.md +101 -0
- package/skills/vue-debug-guides/reference/exact-modifier-for-precise-shortcuts.md +155 -0
- package/skills/vue-debug-guides/reference/fallthrough-attrs-overwrite-vue3.md +159 -0
- package/skills/vue-debug-guides/reference/in-dom-template-parsing-caveats.md +149 -0
- package/skills/vue-debug-guides/reference/inheritattrs-false-for-wrapper-components.md +230 -0
- package/skills/vue-debug-guides/reference/keepalive-router-nested-double-mount.md +222 -0
- package/skills/vue-debug-guides/reference/keepalive-transition-memory-leak.md +144 -0
- package/skills/vue-debug-guides/reference/keyup-modifier-timing.md +137 -0
- package/skills/vue-debug-guides/reference/lifecycle-dom-access-timing.md +216 -0
- package/skills/vue-debug-guides/reference/lifecycle-hooks-synchronous-registration.md +156 -0
- package/skills/vue-debug-guides/reference/lifecycle-ssr-awareness.md +184 -0
- package/skills/vue-debug-guides/reference/local-components-not-in-descendants.md +151 -0
- package/skills/vue-debug-guides/reference/mount-return-value.md +88 -0
- package/skills/vue-debug-guides/reference/multi-root-component-class-attrs.md +93 -0
- package/skills/vue-debug-guides/reference/native-event-collision-with-emits.md +162 -0
- package/skills/vue-debug-guides/reference/no-passive-with-prevent.md +141 -0
- package/skills/vue-debug-guides/reference/no-v-if-with-v-for.md +136 -0
- package/skills/vue-debug-guides/reference/perf-computed-object-stability.md +157 -0
- package/skills/vue-debug-guides/reference/perf-props-stability-update-optimization.md +140 -0
- package/skills/vue-debug-guides/reference/plugin-global-properties-sparingly.md +109 -0
- package/skills/vue-debug-guides/reference/plugin-install-before-mount.md +124 -0
- package/skills/vue-debug-guides/reference/plugin-prefer-provide-inject-over-global-properties.md +120 -0
- package/skills/vue-debug-guides/reference/plugin-typescript-type-augmentation.md +157 -0
- package/skills/vue-debug-guides/reference/prop-defineprops-scope-limitation.md +161 -0
- package/skills/vue-debug-guides/reference/provide-inject-debugging-challenges.md +203 -0
- package/skills/vue-debug-guides/reference/provide-inject-default-value-factory.md +244 -0
- package/skills/vue-debug-guides/reference/provide-inject-reactivity-not-automatic.md +226 -0
- package/skills/vue-debug-guides/reference/provide-inject-synchronous-setup.md +235 -0
- package/skills/vue-debug-guides/reference/reactive-destructuring.md +89 -0
- package/skills/vue-debug-guides/reference/reactivity-debugging-hooks.md +132 -0
- package/skills/vue-debug-guides/reference/reactivity-markraw-for-non-reactive.md +149 -0
- package/skills/vue-debug-guides/reference/reactivity-proxy-identity-hazard.md +96 -0
- package/skills/vue-debug-guides/reference/reactivity-same-tick-batching.md +166 -0
- package/skills/vue-debug-guides/reference/ref-value-access.md +61 -0
- package/skills/vue-debug-guides/reference/refs-in-collections-need-value.md +81 -0
- package/skills/vue-debug-guides/reference/render-function-avoid-internal-vnode-properties.md +151 -0
- package/skills/vue-debug-guides/reference/render-function-vnodes-must-be-unique.md +133 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-h-import-vue3.md +148 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-return-from-setup.md +148 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-slots-as-functions.md +168 -0
- package/skills/vue-debug-guides/reference/rendering-resolve-component-for-string-names.md +231 -0
- package/skills/vue-debug-guides/reference/select-initial-value-ios-bug.md +91 -0
- package/skills/vue-debug-guides/reference/self-referencing-component-name.md +157 -0
- package/skills/vue-debug-guides/reference/sfc-named-exports-forbidden.md +184 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-child-component-styling.md +156 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-dynamic-content.md +193 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-slot-content.md +242 -0
- package/skills/vue-debug-guides/reference/sfc-script-setup-reactivity.md +195 -0
- package/skills/vue-debug-guides/reference/slot-forwarding-to-child-components.md +143 -0
- package/skills/vue-debug-guides/reference/slot-implicit-default-content.md +155 -0
- package/skills/vue-debug-guides/reference/slot-name-reserved-prop.md +109 -0
- package/skills/vue-debug-guides/reference/slot-named-scoped-explicit-default.md +95 -0
- package/skills/vue-debug-guides/reference/slot-render-scope-parent-only.md +135 -0
- package/skills/vue-debug-guides/reference/slot-v-slot-on-components-or-templates-only.md +122 -0
- package/skills/vue-debug-guides/reference/ssr-hydration-mismatch-causes.md +280 -0
- package/skills/vue-debug-guides/reference/ssr-platform-specific-apis.md +256 -0
- package/skills/vue-debug-guides/reference/state-ssr-cross-request-pollution.md +276 -0
- package/skills/vue-debug-guides/reference/suspense-no-builtin-error-handling.md +127 -0
- package/skills/vue-debug-guides/reference/suspense-ssr-hydration-issues.md +159 -0
- package/skills/vue-debug-guides/reference/tailwind-dynamic-class-generation.md +144 -0
- package/skills/vue-debug-guides/reference/teleport-scoped-styles-limitation.md +191 -0
- package/skills/vue-debug-guides/reference/teleport-ssr-hydration.md +152 -0
- package/skills/vue-debug-guides/reference/teleport-target-must-exist.md +113 -0
- package/skills/vue-debug-guides/reference/template-expressions-restrictions.md +114 -0
- package/skills/vue-debug-guides/reference/template-functions-no-side-effects.md +187 -0
- package/skills/vue-debug-guides/reference/template-ref-null-with-v-if.md +123 -0
- package/skills/vue-debug-guides/reference/template-ref-unwrapping-top-level.md +104 -0
- package/skills/vue-debug-guides/reference/template-ref-v-for-order.md +172 -0
- package/skills/vue-debug-guides/reference/textarea-no-interpolation.md +72 -0
- package/skills/vue-debug-guides/reference/transition-group-flip-inline-elements.md +152 -0
- package/skills/vue-debug-guides/reference/transition-group-move-animation-position-absolute.md +130 -0
- package/skills/vue-debug-guides/reference/transition-group-no-default-wrapper-vue3.md +152 -0
- package/skills/vue-debug-guides/reference/transition-js-hooks-done-callback.md +251 -0
- package/skills/vue-debug-guides/reference/transition-nested-duration.md +182 -0
- package/skills/vue-debug-guides/reference/transition-reusable-scoped-style.md +245 -0
- package/skills/vue-debug-guides/reference/transition-router-view-appear.md +193 -0
- package/skills/vue-debug-guides/reference/transition-type-when-mixed.md +172 -0
- package/skills/vue-debug-guides/reference/transition-unmount-hook-timing.md +149 -0
- package/skills/vue-debug-guides/reference/ts-defineprops-boolean-default-false.md +225 -0
- package/skills/vue-debug-guides/reference/ts-defineprops-imported-types-limitations.md +281 -0
- package/skills/vue-debug-guides/reference/ts-event-handler-explicit-typing.md +213 -0
- package/skills/vue-debug-guides/reference/ts-reactive-no-generic-argument.md +196 -0
- package/skills/vue-debug-guides/reference/ts-shallowref-for-dynamic-components.md +218 -0
- package/skills/vue-debug-guides/reference/ts-template-ref-null-handling.md +249 -0
- package/skills/vue-debug-guides/reference/ts-template-type-casting.md +214 -0
- package/skills/vue-debug-guides/reference/ts-withdefaults-mutable-factory-function.md +171 -0
- package/skills/vue-debug-guides/reference/undeclared-emits-double-firing.md +195 -0
- package/skills/vue-debug-guides/reference/use-template-ref-vue35.md +158 -0
- package/skills/vue-debug-guides/reference/v-else-must-follow-v-if.md +136 -0
- package/skills/vue-debug-guides/reference/v-for-component-props.md +95 -0
- package/skills/vue-debug-guides/reference/v-for-computed-reverse-sort.md +86 -0
- package/skills/vue-debug-guides/reference/v-for-key-attribute.md +90 -0
- package/skills/vue-debug-guides/reference/v-for-range-starts-at-one.md +66 -0
- package/skills/vue-debug-guides/reference/v-if-null-check-order.md +171 -0
- package/skills/vue-debug-guides/reference/v-model-ignores-html-attributes.md +83 -0
- package/skills/vue-debug-guides/reference/v-model-ime-composition.md +83 -0
- package/skills/vue-debug-guides/reference/v-model-number-modifier-behavior.md +124 -0
- package/skills/vue-debug-guides/reference/v-show-template-limitation.md +124 -0
- package/skills/vue-debug-guides/reference/watch-async-cleanup.md +180 -0
- package/skills/vue-debug-guides/reference/watch-async-creation-memory-leak.md +176 -0
- package/skills/vue-debug-guides/reference/watch-deep-same-object-reference.md +165 -0
- package/skills/vue-debug-guides/reference/watch-flush-timing.md +189 -0
- package/skills/vue-debug-guides/reference/watch-reactive-property-getter.md +108 -0
- package/skills/vue-debug-guides/reference/watcheffect-async-dependency-tracking.md +173 -0
- package/skills/vue-debug-guides/reference/watcheffect-flush-post-for-refs.md +176 -0
- package/skills/vue-jsx-best-practices/SKILL.md +12 -0
- package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
- package/skills/vue-options-api-best-practices/SKILL.md +23 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
- package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
- package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/vue-router-best-practices/SKILL.md +23 -0
- package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
- package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
- package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
- package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
- package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
- package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
- package/skills/vue-testing-best-practices/SKILL.md +29 -0
- package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
- package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
- package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
- package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
- package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
- package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
- package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
- package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
- package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
- package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
- package/skills/vue-testing-best-practices/reference/testing-vitest-recommended-for-vue.md +204 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TransitionGroup FLIP Animations Do Not Work With Inline Elements
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Move animations silently fail on inline elements, causing items to jump
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, transition-group, animation, flip, css, display, inline-block]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# TransitionGroup FLIP Animations Do Not Work With Inline Elements
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - The FLIP (First, Last, Invert, Play) animation technique that Vue uses for `<TransitionGroup>` move transitions does not work with elements that have `display: inline`. The move animation will silently fail, and items will jump to their new positions instead of smoothly transitioning.
|
|
12
|
+
|
|
13
|
+
This is a CSS limitation, not a Vue bug. CSS transforms (which FLIP uses internally) do not apply to inline elements per the CSS specification.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Ensure list items are not `display: inline` elements
|
|
18
|
+
- [ ] Use `display: inline-block` or `display: block` for list items
|
|
19
|
+
- [ ] Use flexbox or grid containers which make children block-level
|
|
20
|
+
- [ ] Check if inherited styles are setting `display: inline`
|
|
21
|
+
|
|
22
|
+
**Incorrect - Inline elements break move animations:**
|
|
23
|
+
```vue
|
|
24
|
+
<template>
|
|
25
|
+
<!-- BROKEN: span elements are inline by default -->
|
|
26
|
+
<TransitionGroup name="tag" tag="div" class="tag-container">
|
|
27
|
+
<span v-for="tag in tags" :key="tag.id" class="tag">
|
|
28
|
+
{{ tag.name }}
|
|
29
|
+
</span>
|
|
30
|
+
</TransitionGroup>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
.tag-move {
|
|
35
|
+
transition: transform 0.3s ease;
|
|
36
|
+
/* This won't work because spans are inline! */
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Correct - Use inline-block:**
|
|
42
|
+
```vue
|
|
43
|
+
<template>
|
|
44
|
+
<TransitionGroup name="tag" tag="div" class="tag-container">
|
|
45
|
+
<span v-for="tag in tags" :key="tag.id" class="tag">
|
|
46
|
+
{{ tag.name }}
|
|
47
|
+
</span>
|
|
48
|
+
</TransitionGroup>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
.tag {
|
|
53
|
+
display: inline-block; /* REQUIRED for FLIP animations */
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tag-move {
|
|
57
|
+
transition: transform 0.3s ease;
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Correct - Use flexbox container:**
|
|
63
|
+
```vue
|
|
64
|
+
<template>
|
|
65
|
+
<TransitionGroup name="tag" tag="div" class="tag-container">
|
|
66
|
+
<span v-for="tag in tags" :key="tag.id" class="tag">
|
|
67
|
+
{{ tag.name }}
|
|
68
|
+
</span>
|
|
69
|
+
</TransitionGroup>
|
|
70
|
+
</template>
|
|
71
|
+
|
|
72
|
+
<style>
|
|
73
|
+
.tag-container {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
gap: 8px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Flex children are block-level, FLIP works automatically */
|
|
80
|
+
.tag-move {
|
|
81
|
+
transition: transform 0.3s ease;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Correct - Use block elements:**
|
|
87
|
+
```vue
|
|
88
|
+
<template>
|
|
89
|
+
<!-- div elements are block by default -->
|
|
90
|
+
<TransitionGroup name="item" tag="div">
|
|
91
|
+
<div v-for="item in items" :key="item.id" class="item">
|
|
92
|
+
{{ item.name }}
|
|
93
|
+
</div>
|
|
94
|
+
</TransitionGroup>
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<style>
|
|
98
|
+
.item-move {
|
|
99
|
+
transition: transform 0.3s ease;
|
|
100
|
+
}
|
|
101
|
+
</style>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Why Inline Elements Don't Work
|
|
105
|
+
|
|
106
|
+
Per CSS specifications, the `transform` property does not apply to inline boxes. Since FLIP animations use CSS transforms to animate element positions:
|
|
107
|
+
|
|
108
|
+
```css
|
|
109
|
+
/* Vue internally applies something like this during move */
|
|
110
|
+
.item {
|
|
111
|
+
transform: translateX(-50px) translateY(-20px);
|
|
112
|
+
/* Then transitions to transform: none */
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This transform is ignored on inline elements, so no animation occurs.
|
|
117
|
+
|
|
118
|
+
## Elements That Are Inline by Default
|
|
119
|
+
|
|
120
|
+
Be aware of these common inline elements that need `display: inline-block`:
|
|
121
|
+
|
|
122
|
+
- `<span>`
|
|
123
|
+
- `<a>`
|
|
124
|
+
- `<em>`, `<strong>`, `<i>`, `<b>`
|
|
125
|
+
- `<code>`, `<kbd>`
|
|
126
|
+
- `<label>`
|
|
127
|
+
- `<button>` (inline-block by default, but verify)
|
|
128
|
+
|
|
129
|
+
## Move Animations Also Require Transform Transition
|
|
130
|
+
|
|
131
|
+
The `.move` class must have `transform` in its `transition` property:
|
|
132
|
+
|
|
133
|
+
```css
|
|
134
|
+
/* CORRECT */
|
|
135
|
+
.list-move {
|
|
136
|
+
transition: transform 0.3s ease;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* ALSO CORRECT */
|
|
140
|
+
.list-move {
|
|
141
|
+
transition: all 0.3s ease; /* 'all' includes transform */
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* WRONG - transform not included */
|
|
145
|
+
.list-move {
|
|
146
|
+
transition: opacity 0.3s ease; /* Move won't animate! */
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Reference
|
|
151
|
+
- [Vue.js TransitionGroup Move Transitions](https://vuejs.org/guide/built-ins/transition-group.html#move-transitions)
|
|
152
|
+
- [MDN CSS Transform - Formal Definition](https://developer.mozilla.org/en-US/docs/Web/CSS/transform#formal_definition)
|
package/skills/vue-debug-guides/reference/transition-group-move-animation-position-absolute.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TransitionGroup Move Animation Requires Position Absolute on Leaving Items
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Without position absolute, surrounding items jump instead of smoothly animating
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, transition-group, animation, move-transition, css, list-animation]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# TransitionGroup Move Animation Requires Position Absolute on Leaving Items
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - When items are added or removed from a list with `<TransitionGroup>`, surrounding items will instantly "jump" to their new positions instead of smoothly animating. This creates a jarring user experience and is one of the most common mistakes with list animations.
|
|
12
|
+
|
|
13
|
+
The fix is to set `position: absolute` on the `leave-active` class so leaving items are taken out of the layout flow, allowing other items to smoothly animate into their new positions.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Add `.list-move` class (or `.[name]-move`) for smooth repositioning
|
|
18
|
+
- [ ] Set `position: absolute` on `.list-leave-active` class
|
|
19
|
+
- [ ] Ensure the parent container has `position: relative` if needed
|
|
20
|
+
- [ ] Test with rapid add/remove operations to verify smooth animations
|
|
21
|
+
|
|
22
|
+
**Incorrect - Items jump instead of moving:**
|
|
23
|
+
```vue
|
|
24
|
+
<template>
|
|
25
|
+
<TransitionGroup name="list" tag="ul">
|
|
26
|
+
<li v-for="item in items" :key="item.id">
|
|
27
|
+
{{ item.name }}
|
|
28
|
+
</li>
|
|
29
|
+
</TransitionGroup>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
/* INCOMPLETE: Missing move class and position absolute */
|
|
34
|
+
.list-enter-active,
|
|
35
|
+
.list-leave-active {
|
|
36
|
+
transition: all 0.5s ease;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.list-enter-from,
|
|
40
|
+
.list-leave-to {
|
|
41
|
+
opacity: 0;
|
|
42
|
+
transform: translateX(30px);
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Correct - Smooth move transitions:**
|
|
48
|
+
```vue
|
|
49
|
+
<template>
|
|
50
|
+
<TransitionGroup name="list" tag="ul">
|
|
51
|
+
<li v-for="item in items" :key="item.id">
|
|
52
|
+
{{ item.name }}
|
|
53
|
+
</li>
|
|
54
|
+
</TransitionGroup>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<style>
|
|
58
|
+
/* CORRECT: Full set of classes for smooth animations */
|
|
59
|
+
|
|
60
|
+
/* Apply transition to moving elements */
|
|
61
|
+
.list-move,
|
|
62
|
+
.list-enter-active,
|
|
63
|
+
.list-leave-active {
|
|
64
|
+
transition: all 0.5s ease;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.list-enter-from,
|
|
68
|
+
.list-leave-to {
|
|
69
|
+
opacity: 0;
|
|
70
|
+
transform: translateX(30px);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* CRITICAL: Take leaving items out of layout flow */
|
|
74
|
+
.list-leave-active {
|
|
75
|
+
position: absolute;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Why This Works
|
|
81
|
+
|
|
82
|
+
The FLIP animation technique Vue uses internally needs to calculate element positions. When an item leaves:
|
|
83
|
+
|
|
84
|
+
1. Without `position: absolute`: The leaving item still occupies space in the DOM
|
|
85
|
+
2. Other items can't move until the leaving item is fully removed
|
|
86
|
+
3. Result: Items snap to new positions after leave animation completes
|
|
87
|
+
|
|
88
|
+
With `position: absolute`:
|
|
89
|
+
|
|
90
|
+
1. Leaving item is removed from normal layout flow immediately
|
|
91
|
+
2. Other items can begin moving into the vacated space
|
|
92
|
+
3. Result: Leaving animation and move animation happen simultaneously
|
|
93
|
+
|
|
94
|
+
## Visual Diagram
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Without position: absolute:
|
|
98
|
+
[A] [B] [C] [D] <- Remove B
|
|
99
|
+
[A] [C] [D] <- B fading out, C/D waiting
|
|
100
|
+
[A] [C] [D] <- B gone, C/D jump instantly
|
|
101
|
+
|
|
102
|
+
With position: absolute:
|
|
103
|
+
[A] [B] [C] [D] <- Remove B
|
|
104
|
+
[A][B][C] [D] <- B fading (absolute), C/D sliding left
|
|
105
|
+
[A] [C] [D] <- Smooth completion
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Additional Considerations
|
|
109
|
+
|
|
110
|
+
**Container Width:** When using `position: absolute`, items may need explicit widths:
|
|
111
|
+
|
|
112
|
+
```css
|
|
113
|
+
.list-leave-active {
|
|
114
|
+
position: absolute;
|
|
115
|
+
width: 100%; /* Or specific width to maintain layout during leave */
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Stacking Context:** Leaving items with `position: absolute` may stack above other elements:
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
.list-leave-active {
|
|
123
|
+
position: absolute;
|
|
124
|
+
z-index: -1; /* Optional: put behind other items */
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Reference
|
|
129
|
+
- [Vue.js TransitionGroup Move Transitions](https://vuejs.org/guide/built-ins/transition-group.html#move-transitions)
|
|
130
|
+
- [FLIP Animation Technique](https://aerotwist.com/blog/flip-your-animations/)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TransitionGroup No Longer Renders Default Wrapper Element in Vue 3
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Vue 2 to Vue 3 migration may break layouts relying on the default span wrapper
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, transition-group, migration, vue2, breaking-change, wrapper-element]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# TransitionGroup No Longer Renders Default Wrapper Element in Vue 3
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - In Vue 2, `<transition-group>` always rendered a wrapper element (default `<span>`), but in Vue 3, it renders no wrapper element by default thanks to fragment support. This breaking change can cause layout issues and broken styles when migrating from Vue 2.
|
|
12
|
+
|
|
13
|
+
If your code relies on the wrapper element for styling or layout, you must explicitly specify the `tag` prop in Vue 3.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] When migrating from Vue 2, add explicit `tag` prop to all `<TransitionGroup>` components
|
|
18
|
+
- [ ] Review CSS selectors that targeted the wrapper element
|
|
19
|
+
- [ ] Update parent component styles that expected a wrapper element
|
|
20
|
+
- [ ] Consider if the wrapper element is actually needed in Vue 3
|
|
21
|
+
|
|
22
|
+
**Vue 2 Behavior (wrapper element by default):**
|
|
23
|
+
```vue
|
|
24
|
+
<template>
|
|
25
|
+
<transition-group name="list">
|
|
26
|
+
<div v-for="item in items" :key="item.id">{{ item }}</div>
|
|
27
|
+
</transition-group>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<!-- Renders as: -->
|
|
31
|
+
<span> <!-- Default wrapper in Vue 2 -->
|
|
32
|
+
<div>Item 1</div>
|
|
33
|
+
<div>Item 2</div>
|
|
34
|
+
</span>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Vue 3 Behavior (no wrapper by default):**
|
|
38
|
+
```vue
|
|
39
|
+
<template>
|
|
40
|
+
<TransitionGroup name="list">
|
|
41
|
+
<div v-for="item in items" :key="item.id">{{ item }}</div>
|
|
42
|
+
</TransitionGroup>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<!-- Renders as (fragment): -->
|
|
46
|
+
<div>Item 1</div>
|
|
47
|
+
<div>Item 2</div>
|
|
48
|
+
<!-- No wrapper element! -->
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Vue 3 - Explicitly specify wrapper:**
|
|
52
|
+
```vue
|
|
53
|
+
<template>
|
|
54
|
+
<!-- Use tag prop to specify wrapper element -->
|
|
55
|
+
<TransitionGroup name="list" tag="ul">
|
|
56
|
+
<li v-for="item in items" :key="item.id">{{ item }}</li>
|
|
57
|
+
</TransitionGroup>
|
|
58
|
+
</template>
|
|
59
|
+
|
|
60
|
+
<!-- Renders as: -->
|
|
61
|
+
<ul>
|
|
62
|
+
<li>Item 1</li>
|
|
63
|
+
<li>Item 2</li>
|
|
64
|
+
</ul>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Migration Scenarios
|
|
68
|
+
|
|
69
|
+
### Layout Depending on Wrapper
|
|
70
|
+
|
|
71
|
+
**Vue 2 code that breaks in Vue 3:**
|
|
72
|
+
```vue
|
|
73
|
+
<template>
|
|
74
|
+
<transition-group class="grid-container" name="list">
|
|
75
|
+
<div v-for="item in items" :key="item.id">{{ item }}</div>
|
|
76
|
+
</transition-group>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<style>
|
|
80
|
+
.grid-container {
|
|
81
|
+
display: grid;
|
|
82
|
+
grid-template-columns: repeat(3, 1fr);
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
In Vue 3, the class is not applied to anything because there's no wrapper element.
|
|
88
|
+
|
|
89
|
+
**Fixed for Vue 3:**
|
|
90
|
+
```vue
|
|
91
|
+
<template>
|
|
92
|
+
<TransitionGroup class="grid-container" name="list" tag="div">
|
|
93
|
+
<div v-for="item in items" :key="item.id">{{ item }}</div>
|
|
94
|
+
</TransitionGroup>
|
|
95
|
+
</template>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Semantic HTML Lists
|
|
99
|
+
|
|
100
|
+
**Vue 2:**
|
|
101
|
+
```vue
|
|
102
|
+
<transition-group tag="ul" name="list">
|
|
103
|
+
<li v-for="item in items" :key="item.id">{{ item }}</li>
|
|
104
|
+
</transition-group>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Vue 3 (same syntax, but now tag is more important):**
|
|
108
|
+
```vue
|
|
109
|
+
<TransitionGroup tag="ul" name="list">
|
|
110
|
+
<li v-for="item in items" :key="item.id">{{ item }}</li>
|
|
111
|
+
</TransitionGroup>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### When You Don't Need a Wrapper
|
|
115
|
+
|
|
116
|
+
Vue 3's fragment support means you might not need a wrapper at all:
|
|
117
|
+
|
|
118
|
+
```vue
|
|
119
|
+
<template>
|
|
120
|
+
<div class="parent-with-styles">
|
|
121
|
+
<!-- No tag needed if parent handles layout -->
|
|
122
|
+
<TransitionGroup name="fade">
|
|
123
|
+
<span v-for="item in items" :key="item.id">{{ item }}</span>
|
|
124
|
+
</TransitionGroup>
|
|
125
|
+
</div>
|
|
126
|
+
</template>
|
|
127
|
+
|
|
128
|
+
<style>
|
|
129
|
+
.parent-with-styles {
|
|
130
|
+
display: flex;
|
|
131
|
+
gap: 8px;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## In-DOM Template Syntax
|
|
137
|
+
|
|
138
|
+
When using in-DOM templates (not SFCs), remember to use kebab-case:
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<!-- In-DOM template -->
|
|
142
|
+
<transition-group tag="ul" name="list">
|
|
143
|
+
<li v-for="item in items" :key="item.id">{{ item }}</li>
|
|
144
|
+
</transition-group>
|
|
145
|
+
|
|
146
|
+
<!-- NOT -->
|
|
147
|
+
<TransitionGroup tag="ul" name="list"> <!-- Won't work in DOM templates -->
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Reference
|
|
151
|
+
- [Vue 3 Migration Guide - TransitionGroup Root Element](https://v3-migration.vuejs.org/breaking-changes/transition-group.html)
|
|
152
|
+
- [Vue.js TransitionGroup](https://vuejs.org/guide/built-ins/transition-group.html)
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: JavaScript Transition Hooks Require done() Callback with css="false"
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Without calling done(), JavaScript-only transitions complete immediately, skipping the animation entirely
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, transition, javascript, animation, hooks, gsap, done-callback]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# JavaScript Transition Hooks Require done() Callback with css="false"
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - When using JavaScript-only transitions (with `:css="false"`), the `@enter` and `@leave` hooks **must** call the `done()` callback to signal when the animation completes. Without calling `done()`, Vue considers the transition finished immediately, causing elements to appear/disappear without animation.
|
|
12
|
+
|
|
13
|
+
This is especially important when using animation libraries like GSAP, Anime.js, or the Web Animations API.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] When using `:css="false"`, always call `done()` in `@enter` and `@leave` hooks
|
|
18
|
+
- [ ] Call `done()` when your JavaScript animation completes (in the `onComplete` callback)
|
|
19
|
+
- [ ] Remember: `done()` is optional when CSS handles the transition, but **required** with `:css="false"`
|
|
20
|
+
- [ ] Use `:css="false"` to prevent CSS rules from interfering with JS animations
|
|
21
|
+
|
|
22
|
+
**Problematic Code:**
|
|
23
|
+
```vue
|
|
24
|
+
<template>
|
|
25
|
+
<!-- BAD: No done() callback - animation is skipped! -->
|
|
26
|
+
<Transition :css="false" @enter="onEnter" @leave="onLeave">
|
|
27
|
+
<div v-if="show" class="box">Content</div>
|
|
28
|
+
</Transition>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import gsap from 'gsap'
|
|
33
|
+
|
|
34
|
+
function onEnter(el) {
|
|
35
|
+
// Animation starts but Vue doesn't wait for it!
|
|
36
|
+
gsap.from(el, {
|
|
37
|
+
opacity: 0,
|
|
38
|
+
y: 50,
|
|
39
|
+
duration: 0.5
|
|
40
|
+
})
|
|
41
|
+
// Missing done() call - element appears with no animation
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function onLeave(el) {
|
|
45
|
+
gsap.to(el, {
|
|
46
|
+
opacity: 0,
|
|
47
|
+
y: -50,
|
|
48
|
+
duration: 0.5
|
|
49
|
+
})
|
|
50
|
+
// Missing done() call - element removed immediately!
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Correct Code:**
|
|
56
|
+
```vue
|
|
57
|
+
<template>
|
|
58
|
+
<!-- GOOD: done() callback signals animation completion -->
|
|
59
|
+
<Transition :css="false" @enter="onEnter" @leave="onLeave">
|
|
60
|
+
<div v-if="show" class="box">Content</div>
|
|
61
|
+
</Transition>
|
|
62
|
+
</template>
|
|
63
|
+
|
|
64
|
+
<script setup>
|
|
65
|
+
import gsap from 'gsap'
|
|
66
|
+
|
|
67
|
+
function onEnter(el, done) {
|
|
68
|
+
gsap.from(el, {
|
|
69
|
+
opacity: 0,
|
|
70
|
+
y: 50,
|
|
71
|
+
duration: 0.5,
|
|
72
|
+
onComplete: done // Tell Vue animation is complete
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function onLeave(el, done) {
|
|
77
|
+
gsap.to(el, {
|
|
78
|
+
opacity: 0,
|
|
79
|
+
y: -50,
|
|
80
|
+
duration: 0.5,
|
|
81
|
+
onComplete: done // Element removed after animation
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Why Use `:css="false"`?
|
|
88
|
+
|
|
89
|
+
1. **Prevents CSS interference**: Vue won't add transition classes that might conflict
|
|
90
|
+
2. **Slight performance benefit**: Skips CSS transition detection
|
|
91
|
+
3. **Clearer intent**: Makes it explicit that JS controls the animation
|
|
92
|
+
|
|
93
|
+
```vue
|
|
94
|
+
<template>
|
|
95
|
+
<!-- Without :css="false", Vue adds v-enter-active etc. classes -->
|
|
96
|
+
<!-- These can interfere with your JS animation timing -->
|
|
97
|
+
<Transition @enter="onEnter" @leave="onLeave">
|
|
98
|
+
<div v-if="show">May have CSS conflicts</div>
|
|
99
|
+
</Transition>
|
|
100
|
+
|
|
101
|
+
<!-- With :css="false", no classes added - full JS control -->
|
|
102
|
+
<Transition :css="false" @enter="onEnter" @leave="onLeave">
|
|
103
|
+
<div v-if="show">Pure JS animation</div>
|
|
104
|
+
</Transition>
|
|
105
|
+
</template>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Complete JavaScript Transition Example
|
|
109
|
+
|
|
110
|
+
```vue
|
|
111
|
+
<template>
|
|
112
|
+
<Transition
|
|
113
|
+
:css="false"
|
|
114
|
+
@before-enter="onBeforeEnter"
|
|
115
|
+
@enter="onEnter"
|
|
116
|
+
@after-enter="onAfterEnter"
|
|
117
|
+
@enter-cancelled="onEnterCancelled"
|
|
118
|
+
@before-leave="onBeforeLeave"
|
|
119
|
+
@leave="onLeave"
|
|
120
|
+
@after-leave="onAfterLeave"
|
|
121
|
+
@leave-cancelled="onLeaveCancelled"
|
|
122
|
+
>
|
|
123
|
+
<div v-if="show" class="animated-box">Content</div>
|
|
124
|
+
</Transition>
|
|
125
|
+
</template>
|
|
126
|
+
|
|
127
|
+
<script setup>
|
|
128
|
+
import gsap from 'gsap'
|
|
129
|
+
import { ref } from 'vue'
|
|
130
|
+
|
|
131
|
+
const show = ref(false)
|
|
132
|
+
let enterAnimation = null
|
|
133
|
+
let leaveAnimation = null
|
|
134
|
+
|
|
135
|
+
function onBeforeEnter(el) {
|
|
136
|
+
// Set initial state before animation
|
|
137
|
+
el.style.opacity = 0
|
|
138
|
+
el.style.transform = 'translateY(50px)'
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function onEnter(el, done) {
|
|
142
|
+
// Store animation reference for potential cancellation
|
|
143
|
+
enterAnimation = gsap.to(el, {
|
|
144
|
+
opacity: 1,
|
|
145
|
+
y: 0,
|
|
146
|
+
duration: 0.5,
|
|
147
|
+
ease: 'power2.out',
|
|
148
|
+
onComplete: done // REQUIRED with :css="false"
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function onAfterEnter(el) {
|
|
153
|
+
// Cleanup after enter completes
|
|
154
|
+
enterAnimation = null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function onEnterCancelled() {
|
|
158
|
+
// Handle interruption (e.g., user toggles quickly)
|
|
159
|
+
if (enterAnimation) {
|
|
160
|
+
enterAnimation.kill()
|
|
161
|
+
enterAnimation = null
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function onBeforeLeave(el) {
|
|
166
|
+
// Set state before leaving
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function onLeave(el, done) {
|
|
170
|
+
leaveAnimation = gsap.to(el, {
|
|
171
|
+
opacity: 0,
|
|
172
|
+
y: -50,
|
|
173
|
+
duration: 0.5,
|
|
174
|
+
ease: 'power2.in',
|
|
175
|
+
onComplete: done // REQUIRED with :css="false"
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function onAfterLeave(el) {
|
|
180
|
+
leaveAnimation = null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function onLeaveCancelled() {
|
|
184
|
+
if (leaveAnimation) {
|
|
185
|
+
leaveAnimation.kill()
|
|
186
|
+
leaveAnimation = null
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
</script>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Using Web Animations API
|
|
193
|
+
|
|
194
|
+
```vue
|
|
195
|
+
<script setup>
|
|
196
|
+
function onEnter(el, done) {
|
|
197
|
+
const animation = el.animate([
|
|
198
|
+
{ opacity: 0, transform: 'scale(0.9)' },
|
|
199
|
+
{ opacity: 1, transform: 'scale(1)' }
|
|
200
|
+
], {
|
|
201
|
+
duration: 300,
|
|
202
|
+
easing: 'ease-out'
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
animation.onfinish = done // Call done when animation ends
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function onLeave(el, done) {
|
|
209
|
+
const animation = el.animate([
|
|
210
|
+
{ opacity: 1, transform: 'scale(1)' },
|
|
211
|
+
{ opacity: 0, transform: 'scale(0.9)' }
|
|
212
|
+
], {
|
|
213
|
+
duration: 300,
|
|
214
|
+
easing: 'ease-in'
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
animation.onfinish = done
|
|
218
|
+
}
|
|
219
|
+
</script>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Common Mistakes
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
// WRONG: Calling done() immediately instead of after animation
|
|
226
|
+
function onEnter(el, done) {
|
|
227
|
+
gsap.from(el, { opacity: 0, duration: 0.5 })
|
|
228
|
+
done() // Called immediately - animation skipped!
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// WRONG: Forgetting done() parameter
|
|
232
|
+
function onEnter(el) { // No 'done' parameter
|
|
233
|
+
gsap.from(el, {
|
|
234
|
+
opacity: 0,
|
|
235
|
+
onComplete: done // Error: done is not defined!
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// CORRECT: Pass done to animation callback
|
|
240
|
+
function onEnter(el, done) {
|
|
241
|
+
gsap.from(el, {
|
|
242
|
+
opacity: 0,
|
|
243
|
+
duration: 0.5,
|
|
244
|
+
onComplete: done // Called after 0.5s
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Reference
|
|
250
|
+
- [Vue.js Transition - JavaScript Hooks](https://vuejs.org/guide/built-ins/transition.html#javascript-hooks)
|
|
251
|
+
- [GSAP with Vue](https://gsap.com/resources/vue/)
|