Skip to content

Commit 01c9481

Browse files
authored
Merge pull request #7 from holux-design/feat/state-based-animations
add(plugin): onState modifier functionality
2 parents eb640fd + bb91b37 commit 01c9481

File tree

12 files changed

+10987
-43
lines changed

12 files changed

+10987
-43
lines changed

docs/components/DemoStateArray.vue

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<template>
2+
<section
3+
class="w-full h-full relative"
4+
:data-index="currentIndex"
5+
>
6+
<div
7+
v-gsap.onState-index-0.inherit.from="{ scale: 0, duration: 0.3 }"
8+
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 h-[80px] lg:h-[150px] aspect-[3/2] rounded-lg bg-neutral-300 dark:bg-neutral-600 flex items-center justify-center"
9+
>
10+
1
11+
</div>
12+
<div
13+
v-gsap.onState-index-1.inherit.from="{ scale: 0, duration: 0.3 }"
14+
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 h-[80px] lg:h-[150px] aspect-[3/2] rounded-lg bg-neutral-300 dark:bg-neutral-600 flex items-center justify-center"
15+
>
16+
2
17+
</div>
18+
<div
19+
v-gsap.onState-index-2.inherit.from="{ scale: 0, duration: 0.3 }"
20+
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 h-[80px] lg:h-[150px] aspect-[3/2] rounded-lg bg-neutral-300 dark:bg-neutral-600 flex items-center justify-center"
21+
>
22+
3
23+
</div>
24+
25+
<button
26+
class="absolute -bottom-4 left-1/2 -translate-x-1/2 px-3 py-1 rounded-md bg-neutral-300 dark:bg-neutral-600"
27+
@click="increase()"
28+
>
29+
Next Card
30+
</button>
31+
</section>
32+
</template>
33+
34+
<script setup lang="ts">
35+
const currentIndex = ref<number>(0)
36+
37+
function increase() {
38+
currentIndex.value++
39+
if (currentIndex.value > 2) {
40+
currentIndex.value = 0
41+
}
42+
}
43+
</script>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<template>
2+
<div
3+
v-gsap.onState-visible.from="{ scale: 0, rotate: -25, duration: 0.3 }"
4+
:data-visible="isVisible"
5+
class="mt-16 w-[80px] aspect-square rounded-lg bg-neutral-300 dark:bg-neutral-600"
6+
/>
7+
<button
8+
class="px-3 py-1 mt-4 rounded-md bg-neutral-300 dark:bg-neutral-600"
9+
@click="isVisible = !isVisible"
10+
>
11+
{{ isVisible ? 'Hide' : 'Show' }} Element
12+
</button>
13+
</template>
14+
15+
<script setup lang="ts">
16+
const isVisible = ref<boolean>(true)
17+
</script>

docs/components/Snippet.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ const snippets = {
3030
</section>`,
3131
DemoPinned: `<section v-gsap.timeline.pinned>
3232
<div class="Dot" v-gsap.add.to="{ width: '800px' }"></div>
33-
<h1
33+
<h1
3434
v-gsap.add.fromTo="[{ opacity: 0, y: 32 }, { opacity: 1, y: 0 }]"
3535
v-gsap.add.to="{ opacity: 0, y: -32 }">New era of</h1>
36-
<h1
36+
<h1
3737
v-gsap.add.withPrevious.fromTo="[{ opacity: 0, y: 32 }, { opacity: 1, y: 0 }]"
3838
v-gsap.add.to="{ opacity: 0, y: -32 }">animation</h1>
3939
</section>`,
@@ -44,6 +44,14 @@ const snippets = {
4444
DemoTextflowTimeline: `<section v-gsap.timeline.whenVisible="{ start: 'top bottom', end: 'bottom top', scrub: 1.5 }">
4545
<div v-gsap.add.to="{ x: -300 }">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</div>
4646
<div v-gsap.add.withPrevious.to="{ x: 300 }">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</div>
47+
</section>`,
48+
DemoStateToggle: `<div
49+
:data-visible="isVisible"
50+
v-gsap.onState-visible.from="{ scale: 0, rotate: -25 }"></div>`,
51+
DemoStateArray: `<section :data-index="currentIndex">
52+
<div v-gsap.onState-index-0.inherit.from="{ scale: 0 }"></div>
53+
<div v-gsap.onState-index-1.inherit.from="{ scale: 0 }"></div>
54+
<div v-gsap.onState-index-2.inherit.from="{ scale: 0 }"></div>
4755
</section>`,
4856
}
4957

docs/content/2.usage/3.modifiers.md

Lines changed: 90 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ V-GSAP supports the above four main animation types.
1010
- `.to` animates from the current state to a given to-state
1111
- `.fromTo` combines both the above. It takes an Array of two states as value
1212
- `.call` takes an arrow function as value and executes it
13-
- when used with `.whenVisible.`, it will automatically be handled like `.once.`
13+
- when used with `.whenVisible.`, it will automatically be handled like
14+
`.once.`
1415

1516
---
1617

@@ -19,45 +20,99 @@ V-GSAP supports the above four main animation types.
1920
[See in Action](/playground#whenvisible)
2021

2122
- enables scrollTrigger. It defaults to from `top 90%` to `top 50%` and `scrub`
22-
- you can overwrite these properties in the value Object. In combination with `.fromTo`, put the overrides in the second state
23+
- you can overwrite these properties in the value Object. In combination with
24+
`.fromTo`, put the overrides in the second state
2325

2426
### `.fromInvisible`
2527

26-
- Adds `opacity: 0` as initial state and includes `opacity: 1` in `.to` or `.fromTo`.
27-
- This prevents the element from being visible until the directive takes action. Especially needed for above-the-fold animations
28-
- With this modifier you can create entrance animations without always repeating the opacity hassle.
28+
- Adds `opacity: 0` as initial state and includes `opacity: 1` in `.to` or
29+
`.fromTo`.
30+
- This prevents the element from being visible until the directive takes action.
31+
Especially needed for above-the-fold animations
32+
- With this modifier you can create entrance animations without always repeating
33+
the opacity hassle.
2934

3035
### `.once`
3136

32-
Makes the animation play only once when scrolling down, and stay in its final state even when scrolling back up.
37+
Makes the animation play only once when scrolling down, and stay in its final
38+
state even when scrolling back up.
3339

3440
### `.once.reversible`
3541

36-
Similar to `.once` but will reverse the animation when scrolling back up, allowing it to play again when scrolling down.
37-
38-
::alert{type="info"}
39-
This is the default previous behavior of `.once`.
40-
::
42+
Similar to `.once` but will reverse the animation when scrolling back up,
43+
allowing it to play again when scrolling down.
4144

45+
::alert{type="info"} This is the default previous behavior of `.once`. ::
4246

4347
### `.markers`
4448

4549
Adds markers for debugging.
4650

4751
### Custom Scroller
4852

49-
If your content is wrapped in an artificial scroll container, use `{ [...], scroller: '<selector>' }` as a selector string inside the value object to override the default scroll container.
50-
::alert{type="info"}
51-
Note: You can also set this globally in the `nuxt.config.ts` [See details](/installation/configuration)
52-
::
53+
If your content is wrapped in an artificial scroll container, use
54+
`{ [...], scroller: '<selector>' }` as a selector string inside the value object
55+
to override the default scroll container. ::alert{type="info"} Note: You can
56+
also set this globally in the `nuxt.config.ts`
57+
[See details](/installation/configuration) ::
58+
59+
---
60+
61+
## `.onState-<key>-<value>`
62+
63+
The `.onState-` feature allows to play certain animations only when a
64+
data-attribute has a certain value.
65+
66+
The modifier consists of 2 or 3 parts:
67+
68+
`.onState-<key>-<value>`
69+
70+
- `key`: refers to the data attribute that is used as source. Example: key
71+
`test` would refer to `data-test=""`
72+
- `value`: (optional) the target value to trigger the animation.
73+
- If left out, the desired target value is `true`. This allows more readable
74+
syntax like `.onState-open`
75+
76+
Example:
77+
78+
```typescript
79+
<div
80+
:data-index="someIndexVar"
81+
v-gsap.onState-index-2.to="{ ... }"></div>
82+
```
83+
84+
The above example code would run the animation when the `data-index` value is
85+
`2`
86+
87+
This feature uses `MutationObserver` in the background that updates each time
88+
the given data-attribute changes. If the current value equals the target value,
89+
the animation is played. If the values don't match, the animation is reversed.
90+
91+
### .inherit
92+
93+
This is a submodifier for `.onState-` and allows the data-attribute to be on
94+
some parent. This is useful if you want to update multiple child elements based
95+
on some parent state.
96+
97+
In the background `el.closest()` is used to search for a parent that has the
98+
matching data-attribute.
99+
100+
Simple example:
101+
102+
```typescript
103+
<div :data-index="someIndexVar">
104+
<div v-gsap.onState-index-2.inherit.to="{ ... }"></div>
105+
</div>
106+
```
53107

54108
---
55109

56110
## `.parallax`
57111

58112
[See in Action](/playground#parallax)
59113

60-
- enables parallax effect for this element. Brings its own scrollTrigger, therefore doesn't need extra `.whenVisible.`.
114+
- enables parallax effect for this element. Brings its own scrollTrigger,
115+
therefore doesn't need extra `.whenVisible.`.
61116
- Only works with a speed settings like:
62117

63118
### `.slower-<integer>`
@@ -66,17 +121,19 @@ Note: You can also set this globally in the `nuxt.config.ts` [See details](/inst
66121

67122
Element scrolls slower or faster than the default scroll speed.
68123

69-
- The value is a multiplier of 10% element height. E.g. `.slower-5` scrolls the element from +-50% to +-50% height.
70-
- If the value is left out, default is `5`. Meaning: `.slower` defaults to `.slower-5` (same with faster)
124+
- The value is a multiplier of 10% element height. E.g. `.slower-5` scrolls the
125+
element from +-50% to +-50% height.
126+
- If the value is left out, default is `5`. Meaning: `.slower` defaults to
127+
`.slower-5` (same with faster)
71128

72129
---
73130

74131
## `.stagger`
75132

76133
[See in Action](/playground#stagger)
77134

78-
enables stagger for immediate children if placed on the parent.
79-
Duration can be overwritten in the value object.
135+
enables stagger for immediate children if placed on the parent. Duration can be
136+
overwritten in the value object.
80137

81138
---
82139

@@ -95,15 +152,14 @@ This sets the repeat of the timeline to `-1` to infinitely repeat the animation
95152
- can be added to delay the action for n milliseconds like `.delay-500.`
96153
- any integer value will be accepted, like `.delay-1234.`
97154

98-
::callout{type="warning"}
99-
#summary
100-
Note: Only works with `whenVisible` if used with `once`
155+
::callout{type="warning"} #summary Note: Only works with `whenVisible` if used
156+
with `once`
101157

102-
#content
103-
Since `whenVisible` is specifically timed via `start` and `end`, a delay only squeezes the actual animation towards `end`.
158+
#content Since `whenVisible` is specifically timed via `start` and `end`, a
159+
delay only squeezes the actual animation towards `end`.
104160

105-
With `.whenVisible.once`, the animation is no longer "timed" but "triggered", therefore `.delay-` makes sense again
106-
::
161+
With `.whenVisible.once`, the animation is no longer "timed" but "triggered",
162+
therefore `.delay-` makes sense again ::
107163

108164
---
109165

@@ -174,10 +230,8 @@ Makes an element be magnetically attracted to the cursor
174230

175231
### `.weak`, `.weaker`, `.stronger`, `.strong`
176232

177-
Modifies the strength of attraction.
178-
::alert{type="info"}
179-
Order: weak <- weaker <- **default (none)** -> stronger -> strong
180-
::
233+
Modifies the strength of attraction. ::alert{type="info"} Order: weak <- weaker
234+
<- **default (none)** -> stronger -> strong ::
181235

182236
### `.refuse`
183237

@@ -195,7 +249,8 @@ With these you can specify the viewport on which the animation shall take place.
195249

196250
- Window resizing is handled automatically.
197251
- Breakpoint is `768px`
198-
- You can combine both. Simply use both `v-gsap.mobile.` AND `v-gsap.desktop.` on the same element.
252+
- You can combine both. Simply use both `v-gsap.mobile.` AND `v-gsap.desktop.`
253+
on the same element.
199254

200255
---
201256

@@ -229,10 +284,9 @@ There are the following options out of the box:
229284
- `.scale-full`
230285
- Fades and scales in from 0% size
231286

232-
::alert{type="info"}
233-
Note: `.entrance` uses `.whenVisible.once` under the hood.
287+
::alert{type="info"} Note: `.entrance` uses `.whenVisible.once` under the hood.
234288

235-
`.once` is needed to ensure animations run through even if scrollTrigger start is passed (e.g. in a hero section)
236-
::
289+
`.once` is needed to ensure animations run through even if scrollTrigger start
290+
is passed (e.g. in a hero section) ::
237291

238292
---

docs/pages/playground.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@
4646
/>
4747
</div>
4848
</div>
49+
<div class="Grid !grid-cols-1 lg:!grid-cols-2 mt-20">
50+
<BoxComponent class="w-full">
51+
<DemoStateToggle />
52+
</BoxComponent>
53+
<Snippet
54+
name="DemoStateToggle"
55+
class="w-full"
56+
/>
57+
</div>
58+
<div class="Grid !grid-cols-1 lg:!grid-cols-2 mt-20">
59+
<BoxComponent class="w-full">
60+
<DemoStateArray />
61+
</BoxComponent>
62+
<Snippet
63+
name="DemoStateArray"
64+
class="w-full"
65+
/>
66+
</div>
4967
<div
5068
v-gsap.timeline.pinned="{ end: '+=1500px' }"
5169
class="Grid !grid-cols-1 lg:!grid-cols-2 mt-20"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "v-gsap-nuxt",
3-
"version": "1.2.2",
3+
"version": "1.2.9",
44
"description": "GSAP Directive for Nuxt 3",
55
"repository": "holux-design/v-gsap-nuxt",
66
"homepage": "https://v-gsap-nuxt.vercel.app",

playground/assets/css/tailwind.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;

playground/nuxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export default defineNuxtConfig({
2-
modules: ['../src/module'],
2+
modules: ['../src/module', '@nuxtjs/tailwindcss'],
33
devtools: { enabled: true },
44
compatibilityDate: '2024-11-28',
55

0 commit comments

Comments
 (0)