@vikeriait/vue-viewport 1.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/LICENSE +21 -0
- package/README.md +294 -0
- package/dist/index.d.ts +300 -0
- package/dist/style.css +1 -0
- package/dist/vue-viewport.js +330 -0
- package/dist/vue-viewport.umd.cjs +1 -0
- package/package.json +78 -0
- package/src/styles/presets.css +258 -0
- package/src/styles/tailwind.css +8 -0
- package/web-types.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vikeria Srl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# @vikeriait/vue-viewport
|
|
2
|
+
|
|
3
|
+
A lightweight, high-performance Vue 3 directive and composable to detect when elements enter the viewport, featuring "Smart Presets", automatic margin compensation for smooth reveals, and seamless Tailwind CSS integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 👁 **Viewport Detection**: Track when elements enter or leave with precision.
|
|
8
|
+
- 🎨 **Tailwind Presets**: Ready-to-use animations like `fade-up`, `scale-up`, `slide-left`.
|
|
9
|
+
- 🧠 **Smart Presets**: `fade-y` and `slide-y` automatically adapt direction based on scroll.
|
|
10
|
+
- 📐 **Auto RootMargin**: Intelligently calculates the optimal `rootMargin` based on your applied styles (e.g., `translate-y-20` or `--viewport-distance`) to ensure animations start exactly when the element enters, preventing "late" triggers or blank spaces.
|
|
11
|
+
- 🧩 **Modular**: Use presets via the directive and control timing (delay, duration) via standard Tailwind classes.
|
|
12
|
+
- 🔀 **Conditional Logic**: Apply animations only when scrolling `down` or `up`.
|
|
13
|
+
- 🛠 **Customizable**: Works with custom CSS, inline styles (`style="transform:..."`), and Tailwind classes.
|
|
14
|
+
- 📦 **Composable**: `useInViewport` for programmatic control.
|
|
15
|
+
- ♿ **Accessibility**: Respects `prefers-reduced-motion` out of the box.
|
|
16
|
+
- 🦾 **TypeScript**: Fully typed.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @vikeriait/vue-viewport
|
|
22
|
+
# or
|
|
23
|
+
npm install @vikeriait/vue-viewport
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
### 1. Global Plugin Registration
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { createApp } from 'vue'
|
|
32
|
+
import ViewportPlugin from '@vikeriait/vue-viewport'
|
|
33
|
+
|
|
34
|
+
const app = createApp(App)
|
|
35
|
+
|
|
36
|
+
app.use(ViewportPlugin)
|
|
37
|
+
app.mount('#app')
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Import Styles
|
|
41
|
+
|
|
42
|
+
**For Tailwind CSS (v4) users:**
|
|
43
|
+
Import the library's Tailwind configuration to enable the presets and custom variants (like `inviewport:`).
|
|
44
|
+
|
|
45
|
+
```css
|
|
46
|
+
@import "tailwindcss";
|
|
47
|
+
|
|
48
|
+
@import "@vikeriait/vue-viewport";
|
|
49
|
+
/* or explicitly: @import "@vikeriait/vue-viewport/tailwind"; */
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**For Standard CSS users:**
|
|
53
|
+
If you don't use Tailwind, import the CSS bundle to enable the built-in presets.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import '@vikeriait/vue-viewport/css'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
### Directive `v-viewport`
|
|
62
|
+
|
|
63
|
+
#### 1. Basic Presets
|
|
64
|
+
Simply pass the preset name string.
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<div v-viewport="'fade-up'">
|
|
68
|
+
I will fade up when scrolled into view!
|
|
69
|
+
</div>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### 2. Utility Classes
|
|
73
|
+
If you want to add Tailwind (or custom) utility classes like delays or durations, apply them directly to the `class` attribute. The directive handles the preset logic separately.
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<!-- ✅ CORRECT: Separate preset and utility classes -->
|
|
77
|
+
<div v-viewport="'fade-up'" class="delay-200 duration-1000">
|
|
78
|
+
I appear slowly with a delay.
|
|
79
|
+
</div>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### 3. Smart Presets (`-y`)
|
|
83
|
+
These presets automatically detect the scroll direction and animate accordingly (e.g., slide up when scrolling down, slide down when scrolling up).
|
|
84
|
+
|
|
85
|
+
```html
|
|
86
|
+
<div v-viewport="'fade-y'">
|
|
87
|
+
I enter naturally from whichever side you scroll from.
|
|
88
|
+
</div>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### 4. Directional Modifiers
|
|
92
|
+
Restrict animations to a specific scroll direction by appending `-down` (scrolling down / enter from bottom) or `-up` (scrolling up / enter from top).
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<!-- Animates only when you scroll DOWN to it -->
|
|
96
|
+
<div v-viewport="'fade-up-down'">
|
|
97
|
+
I appear only on normal scroll.
|
|
98
|
+
</div>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### 5. Staggering
|
|
102
|
+
Delay animations for elements entering at the same time. The library guarantees DOM order execution.
|
|
103
|
+
|
|
104
|
+
You can pass:
|
|
105
|
+
- `number`: Delay in milliseconds (e.g. `100`).
|
|
106
|
+
- `string`: CSS time value (e.g. `'200ms'`, `'0.1s'`).
|
|
107
|
+
- `true`: Uses the global default `--viewport-stagger` (default: 100ms).
|
|
108
|
+
|
|
109
|
+
*Note: The stagger delay is **additive** to any existing CSS delay.*
|
|
110
|
+
|
|
111
|
+
```html
|
|
112
|
+
<!-- Explicit value -->
|
|
113
|
+
<div v-for="i in 6" v-viewport="{ animation: 'fade-up', stagger: 100 }"></div>
|
|
114
|
+
|
|
115
|
+
<!-- Use default from CSS variable -->
|
|
116
|
+
<div v-for="i in 6" v-viewport="{ animation: 'fade-up', stagger: true }"></div>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### 6. Inline Timing Control
|
|
120
|
+
You can override the global defaults or utility classes directly via options. This is useful for dynamic values.
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<div v-viewport="{ animation: 'fade-up', duration: 1000, delay: 500, easing: 'ease-in-out' }">
|
|
124
|
+
I have custom timing!
|
|
125
|
+
</div>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Component `<VViewport />`
|
|
129
|
+
|
|
130
|
+
Prefer components over directives? We got you.
|
|
131
|
+
|
|
132
|
+
**Global Usage:**
|
|
133
|
+
The plugin registers the component globally as `<VViewport />`.
|
|
134
|
+
|
|
135
|
+
```html
|
|
136
|
+
<VViewport preset="fade-up">
|
|
137
|
+
<h1>Hello World</h1>
|
|
138
|
+
</VViewport>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Local Import:**
|
|
142
|
+
You can also import it directly (exported as `Viewport`).
|
|
143
|
+
|
|
144
|
+
```html
|
|
145
|
+
<script setup>
|
|
146
|
+
import { Viewport } from '@vikeriait/vue-viewport'
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<template>
|
|
150
|
+
<Viewport as="section" preset="fade-up" :stagger="100">
|
|
151
|
+
...
|
|
152
|
+
</Viewport>
|
|
153
|
+
</template>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Props:**
|
|
157
|
+
- `as`: (string) HTML tag (default: 'div').
|
|
158
|
+
- `preset`: (string) Animation name.
|
|
159
|
+
- `stagger`: (number/string/boolean) Delay logic.
|
|
160
|
+
- `duration`: (number/string) Custom duration.
|
|
161
|
+
- `delay`: (number/string) Custom delay.
|
|
162
|
+
- `easing`: (string) Custom easing.
|
|
163
|
+
- `once`: (boolean) Disconnect after first entry.
|
|
164
|
+
- `threshold`: (number) Intersection threshold.
|
|
165
|
+
- `rootMargin`: (string) Custom margin.
|
|
166
|
+
|
|
167
|
+
### Advanced: Tailwind & Custom Styles
|
|
168
|
+
|
|
169
|
+
The library is **smart**. If you use custom transforms (via Tailwind classes or inline styles), it detects them and automatically adjusts the IntersectionObserver's `rootMargin` to ensure the animation triggers precisely when the *visual* element enters the screen.
|
|
170
|
+
|
|
171
|
+
**Example with Tailwind:**
|
|
172
|
+
The library sees `translate-y-20` (80px), calculates the offset, and triggers the animation 80px earlier to prevent any "blank space" or jump.
|
|
173
|
+
|
|
174
|
+
```html
|
|
175
|
+
<div
|
|
176
|
+
v-viewport
|
|
177
|
+
class="opacity-0 translate-y-20 transition-all duration-700
|
|
178
|
+
inviewport:opacity-100 inviewport:translate-y-0"
|
|
179
|
+
>
|
|
180
|
+
I am a fully custom Tailwind animation handled perfectly.
|
|
181
|
+
</div>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Example with Inline Style:**
|
|
185
|
+
|
|
186
|
+
```html
|
|
187
|
+
<div
|
|
188
|
+
v-viewport
|
|
189
|
+
style="opacity: 0; transform: translateY(100px); transition: 1s"
|
|
190
|
+
class="inviewport:opacity-100 inviewport:translate-y-0"
|
|
191
|
+
>
|
|
192
|
+
I work too!
|
|
193
|
+
</div>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Composable `useInViewport`
|
|
197
|
+
|
|
198
|
+
```vue
|
|
199
|
+
<script setup lang="ts">
|
|
200
|
+
import { ref } from 'vue'
|
|
201
|
+
import { useInViewport } from '@vikeriait/vue-viewport'
|
|
202
|
+
|
|
203
|
+
const target = ref(null)
|
|
204
|
+
const { isInView, entry } = useInViewport(target)
|
|
205
|
+
</script>
|
|
206
|
+
|
|
207
|
+
<template>
|
|
208
|
+
<div ref="target">
|
|
209
|
+
{{ isInView ? 'Visible' : 'Hidden' }}
|
|
210
|
+
</div>
|
|
211
|
+
</template>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Built-in Presets
|
|
215
|
+
|
|
216
|
+
| Preset | Description |
|
|
217
|
+
|:-------------------|:----------------|
|
|
218
|
+
| `fade` | Simple fade |
|
|
219
|
+
| `fade-up/down` | Fade + Slide Y |
|
|
220
|
+
| `fade-left/right` | Fade + Slide X |
|
|
221
|
+
| `slide-up/down` | Slide Y |
|
|
222
|
+
| `slide-left/right` | Slide X |
|
|
223
|
+
| `scale-up/down` | Scale Transform |
|
|
224
|
+
| `blur-in` | Blur Reveal |
|
|
225
|
+
|
|
226
|
+
*Note: All presets support `-down` (on scroll down) and `-up` (on scroll up) suffixes.*
|
|
227
|
+
|
|
228
|
+
## CSS Variables
|
|
229
|
+
|
|
230
|
+
You can customize the global defaults in your CSS:
|
|
231
|
+
|
|
232
|
+
```css
|
|
233
|
+
:root {
|
|
234
|
+
--viewport-duration: 0.6s;
|
|
235
|
+
--viewport-ease: ease-out;
|
|
236
|
+
--viewport-delay: 0s; /* Global default delay */
|
|
237
|
+
--viewport-distance: 2rem;
|
|
238
|
+
--viewport-stagger: 100ms;
|
|
239
|
+
--viewport-scale-in: 0.95;
|
|
240
|
+
--viewport-scale-out: 1.05;
|
|
241
|
+
--viewport-blur: 12px;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Accessibility & Print
|
|
246
|
+
|
|
247
|
+
The library is designed to be inclusive and reliable:
|
|
248
|
+
|
|
249
|
+
- **Reduced Motion**: If a user has enabled "Reduce Motion" in their system settings, the library automatically detects the `@media (prefers-reduced-motion: reduce)` preference. In this case, all animations are disabled, and elements appear instantly in their final state (`opacity: 1`, `transform: none`).
|
|
250
|
+
- **Print Friendly**: When printing a page, all animations and initial "hidden" states are stripped away via `@media print`. This ensures that all content is fully visible and correctly positioned on the physical or PDF page.
|
|
251
|
+
|
|
252
|
+
## API Reference
|
|
253
|
+
|
|
254
|
+
### Plugin Options
|
|
255
|
+
|
|
256
|
+
Options passed to `app.use(ViewportPlugin, options)`.
|
|
257
|
+
|
|
258
|
+
| Option | Type | Default | Description |
|
|
259
|
+
| :--- | :--- | :--- | :--- |
|
|
260
|
+
| `presets` | `Record<string, string>` | `DEFAULT_PRESETS` | Custom presets map. |
|
|
261
|
+
|
|
262
|
+
### Directive Options
|
|
263
|
+
|
|
264
|
+
Options object passed as `v-viewport="{ ... }"`.
|
|
265
|
+
|
|
266
|
+
| Option | Type | Default | Description |
|
|
267
|
+
| :--- | :--- | :--- | :--- |
|
|
268
|
+
| `animation` | `string` | `undefined` | The animation class or preset name(s). |
|
|
269
|
+
| `stagger` | `number \| string \| boolean` | `undefined` | Delay (ms/string) or `true` for global default. |
|
|
270
|
+
| `duration` | `number \| string` | `undefined` | Custom duration. |
|
|
271
|
+
| `delay` | `number \| string` | `undefined` | Custom delay. |
|
|
272
|
+
| `easing` | `string` | `undefined` | Custom easing function. |
|
|
273
|
+
| `once` | `boolean` | `false` | Disconnect after first entry. |
|
|
274
|
+
| `threshold` | `number \| number[]` | `0.1` | IntersectionObserver threshold. |
|
|
275
|
+
| `rootMargin` | `string` | `auto` | Manually override the calculated margin. |
|
|
276
|
+
| `onEnter` | `function` | `-` | Callback when entering viewport. |
|
|
277
|
+
| `onLeave` | `function` | `-` | Callback when leaving viewport. |
|
|
278
|
+
|
|
279
|
+
### Directive Modifiers
|
|
280
|
+
|
|
281
|
+
| Modifier | Description |
|
|
282
|
+
| :--- | :--- |
|
|
283
|
+
| `.once` | Disconnect after first entry. |
|
|
284
|
+
|
|
285
|
+
### Directive Events
|
|
286
|
+
|
|
287
|
+
| Event | Type | Description |
|
|
288
|
+
| :--- | :--- | :--- |
|
|
289
|
+
| `@view-enter` | `ViewportEvent` | Dispatched when entering viewport. |
|
|
290
|
+
| `@view-leave` | `ViewportEvent` | Dispatched when leaving viewport. |
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { ComponentOptionsMixin } from 'vue';
|
|
2
|
+
import { ComponentProvideOptions } from 'vue';
|
|
3
|
+
import { DefineComponent } from 'vue';
|
|
4
|
+
import { Directive } from 'vue';
|
|
5
|
+
import { ExtractPropTypes } from 'vue';
|
|
6
|
+
import { Plugin as Plugin_2 } from 'vue';
|
|
7
|
+
import { PropType } from 'vue';
|
|
8
|
+
import { PublicProps } from 'vue';
|
|
9
|
+
import { Ref } from 'vue';
|
|
10
|
+
|
|
11
|
+
declare const __VLS_component: DefineComponent<ExtractPropTypes< {
|
|
12
|
+
/** The HTML tag to render. Defaults to 'div'. */
|
|
13
|
+
as: {
|
|
14
|
+
type: StringConstructor;
|
|
15
|
+
default: string;
|
|
16
|
+
};
|
|
17
|
+
/** The animation preset name (e.g., 'fade-up'). */
|
|
18
|
+
preset: {
|
|
19
|
+
type: StringConstructor;
|
|
20
|
+
default: undefined;
|
|
21
|
+
};
|
|
22
|
+
/** Delay in ms or CSS string (e.g. '100ms'). Set to true to use global variable. */
|
|
23
|
+
stagger: {
|
|
24
|
+
type: PropType<ViewportOptions["stagger"]>;
|
|
25
|
+
default: undefined;
|
|
26
|
+
};
|
|
27
|
+
/** Intersection threshold (0.0 - 1.0). */
|
|
28
|
+
threshold: {
|
|
29
|
+
type: PropType<ViewportOptions["threshold"]>;
|
|
30
|
+
default: undefined;
|
|
31
|
+
};
|
|
32
|
+
/** Custom rootMargin string. */
|
|
33
|
+
rootMargin: {
|
|
34
|
+
type: PropType<ViewportOptions["rootMargin"]>;
|
|
35
|
+
default: undefined;
|
|
36
|
+
};
|
|
37
|
+
/** If true, disconnects the observer after the first entry. */
|
|
38
|
+
once: {
|
|
39
|
+
type: BooleanConstructor;
|
|
40
|
+
default: boolean;
|
|
41
|
+
};
|
|
42
|
+
/** Custom root element (defaults to viewport). */
|
|
43
|
+
root: {
|
|
44
|
+
type: PropType<ViewportOptions["root"]>;
|
|
45
|
+
default: undefined;
|
|
46
|
+
};
|
|
47
|
+
/** Custom duration for the animation. */
|
|
48
|
+
duration: {
|
|
49
|
+
type: PropType<ViewportOptions["duration"]>;
|
|
50
|
+
default: undefined;
|
|
51
|
+
};
|
|
52
|
+
/** Custom delay for the animation. */
|
|
53
|
+
delay: {
|
|
54
|
+
type: PropType<ViewportOptions["delay"]>;
|
|
55
|
+
default: undefined;
|
|
56
|
+
};
|
|
57
|
+
/** Custom easing for the animation. */
|
|
58
|
+
easing: {
|
|
59
|
+
type: StringConstructor;
|
|
60
|
+
default: undefined;
|
|
61
|
+
};
|
|
62
|
+
}>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
|
|
63
|
+
enter: (payload: {
|
|
64
|
+
entry: IntersectionObserverEntry;
|
|
65
|
+
direction: ViewportDirection;
|
|
66
|
+
}) => any;
|
|
67
|
+
leave: (payload: {
|
|
68
|
+
entry: IntersectionObserverEntry;
|
|
69
|
+
direction: ViewportDirection;
|
|
70
|
+
}) => any;
|
|
71
|
+
}, string, PublicProps, Readonly<ExtractPropTypes< {
|
|
72
|
+
/** The HTML tag to render. Defaults to 'div'. */
|
|
73
|
+
as: {
|
|
74
|
+
type: StringConstructor;
|
|
75
|
+
default: string;
|
|
76
|
+
};
|
|
77
|
+
/** The animation preset name (e.g., 'fade-up'). */
|
|
78
|
+
preset: {
|
|
79
|
+
type: StringConstructor;
|
|
80
|
+
default: undefined;
|
|
81
|
+
};
|
|
82
|
+
/** Delay in ms or CSS string (e.g. '100ms'). Set to true to use global variable. */
|
|
83
|
+
stagger: {
|
|
84
|
+
type: PropType<ViewportOptions["stagger"]>;
|
|
85
|
+
default: undefined;
|
|
86
|
+
};
|
|
87
|
+
/** Intersection threshold (0.0 - 1.0). */
|
|
88
|
+
threshold: {
|
|
89
|
+
type: PropType<ViewportOptions["threshold"]>;
|
|
90
|
+
default: undefined;
|
|
91
|
+
};
|
|
92
|
+
/** Custom rootMargin string. */
|
|
93
|
+
rootMargin: {
|
|
94
|
+
type: PropType<ViewportOptions["rootMargin"]>;
|
|
95
|
+
default: undefined;
|
|
96
|
+
};
|
|
97
|
+
/** If true, disconnects the observer after the first entry. */
|
|
98
|
+
once: {
|
|
99
|
+
type: BooleanConstructor;
|
|
100
|
+
default: boolean;
|
|
101
|
+
};
|
|
102
|
+
/** Custom root element (defaults to viewport). */
|
|
103
|
+
root: {
|
|
104
|
+
type: PropType<ViewportOptions["root"]>;
|
|
105
|
+
default: undefined;
|
|
106
|
+
};
|
|
107
|
+
/** Custom duration for the animation. */
|
|
108
|
+
duration: {
|
|
109
|
+
type: PropType<ViewportOptions["duration"]>;
|
|
110
|
+
default: undefined;
|
|
111
|
+
};
|
|
112
|
+
/** Custom delay for the animation. */
|
|
113
|
+
delay: {
|
|
114
|
+
type: PropType<ViewportOptions["delay"]>;
|
|
115
|
+
default: undefined;
|
|
116
|
+
};
|
|
117
|
+
/** Custom easing for the animation. */
|
|
118
|
+
easing: {
|
|
119
|
+
type: StringConstructor;
|
|
120
|
+
default: undefined;
|
|
121
|
+
};
|
|
122
|
+
}>> & Readonly<{
|
|
123
|
+
onEnter?: ((payload: {
|
|
124
|
+
entry: IntersectionObserverEntry;
|
|
125
|
+
direction: ViewportDirection;
|
|
126
|
+
}) => any) | undefined;
|
|
127
|
+
onLeave?: ((payload: {
|
|
128
|
+
entry: IntersectionObserverEntry;
|
|
129
|
+
direction: ViewportDirection;
|
|
130
|
+
}) => any) | undefined;
|
|
131
|
+
}>, {
|
|
132
|
+
root: Element | Document | null | undefined;
|
|
133
|
+
rootMargin: string | undefined;
|
|
134
|
+
threshold: number | number[] | undefined;
|
|
135
|
+
stagger: string | number | boolean | undefined;
|
|
136
|
+
duration: string | number | undefined;
|
|
137
|
+
delay: string | number | undefined;
|
|
138
|
+
easing: string;
|
|
139
|
+
once: boolean;
|
|
140
|
+
as: string;
|
|
141
|
+
preset: string;
|
|
142
|
+
}, {}, {}, {}, string, ComponentProvideOptions, true, {}, any>;
|
|
143
|
+
|
|
144
|
+
declare function __VLS_template(): {
|
|
145
|
+
attrs: Partial<{}>;
|
|
146
|
+
slots: {
|
|
147
|
+
default?(_: {}): any;
|
|
148
|
+
};
|
|
149
|
+
refs: {};
|
|
150
|
+
rootEl: any;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
declare type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
154
|
+
|
|
155
|
+
declare type __VLS_WithTemplateSlots<T, S> = T & {
|
|
156
|
+
new (): {
|
|
157
|
+
$slots: S;
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Interface for the resolved configuration used internally by the directive.
|
|
163
|
+
*/
|
|
164
|
+
export declare interface ResolvedConfig {
|
|
165
|
+
options: ViewportOptions;
|
|
166
|
+
animationName: string | undefined;
|
|
167
|
+
once: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* A composable that tracks whether an element is within the viewport.
|
|
172
|
+
*
|
|
173
|
+
* @param target - The ref of the HTML element to observe.
|
|
174
|
+
* @param options - IntersectionObserver options.
|
|
175
|
+
* @returns Object containing `isInView` boolean ref and the `entry` object.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```ts
|
|
179
|
+
* const target = ref(null)
|
|
180
|
+
* const { isInView } = useInViewport(target)
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export declare function useInViewport(target: Ref<HTMLElement | null>, options?: IntersectionObserverInit): UseInViewportReturn;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Return value of the useInViewport composable.
|
|
187
|
+
*/
|
|
188
|
+
export declare interface UseInViewportReturn {
|
|
189
|
+
/**
|
|
190
|
+
* Boolean ref indicating if the element is currently in the viewport.
|
|
191
|
+
*/
|
|
192
|
+
isInView: Ref<boolean>;
|
|
193
|
+
/**
|
|
194
|
+
* The current IntersectionObserverEntry, or null if not yet observed.
|
|
195
|
+
*/
|
|
196
|
+
entry: Ref<IntersectionObserverEntry | null>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export declare const Viewport: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Direction of the scroll movement relative to the viewport.
|
|
203
|
+
*/
|
|
204
|
+
export declare type ViewportDirection = 'down' | 'up';
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Custom event emitted by the directive on enter/leave.
|
|
208
|
+
*/
|
|
209
|
+
export declare type ViewportEvent = CustomEvent<ViewportEventDetail>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Detail object for Viewport custom events.
|
|
213
|
+
*/
|
|
214
|
+
export declare interface ViewportEventDetail {
|
|
215
|
+
entry: IntersectionObserverEntry;
|
|
216
|
+
direction: ViewportDirection;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Options for the v-viewport directive.
|
|
221
|
+
* Extends IntersectionObserverInit to allow custom observer configuration.
|
|
222
|
+
*/
|
|
223
|
+
export declare interface ViewportOptions extends IntersectionObserverInit {
|
|
224
|
+
/**
|
|
225
|
+
* The preset name(s) to apply (e.g. 'fade-up').
|
|
226
|
+
* Custom utility classes should be applied via the standard 'class' attribute.
|
|
227
|
+
*/
|
|
228
|
+
animation?: string;
|
|
229
|
+
/**
|
|
230
|
+
* Stagger delay for elements entering together.
|
|
231
|
+
* - `number`: milliseconds
|
|
232
|
+
* - `string`: CSS time (e.g. '100ms', '0.1s')
|
|
233
|
+
* - `true`: uses global `--viewport-stagger` variable
|
|
234
|
+
*/
|
|
235
|
+
stagger?: number | string | boolean;
|
|
236
|
+
/**
|
|
237
|
+
* Custom duration for the animation.
|
|
238
|
+
* - `number`: milliseconds
|
|
239
|
+
* - `string`: CSS time (e.g. '1000ms', '1s')
|
|
240
|
+
*/
|
|
241
|
+
duration?: number | string;
|
|
242
|
+
/**
|
|
243
|
+
* Custom delay for the animation.
|
|
244
|
+
* - `number`: milliseconds
|
|
245
|
+
* - `string`: CSS time (e.g. '500ms', '0.5s')
|
|
246
|
+
*/
|
|
247
|
+
delay?: number | string;
|
|
248
|
+
/**
|
|
249
|
+
* Custom easing for the animation (e.g. 'ease-in-out', 'cubic-bezier(...)').
|
|
250
|
+
*/
|
|
251
|
+
easing?: string;
|
|
252
|
+
/**
|
|
253
|
+
* If true, the observer will disconnect after the first entry.
|
|
254
|
+
*/
|
|
255
|
+
once?: boolean;
|
|
256
|
+
/**
|
|
257
|
+
* Callback triggered when the element enters the viewport.
|
|
258
|
+
*/
|
|
259
|
+
onEnter?: (entry: IntersectionObserverEntry, direction: ViewportDirection) => void;
|
|
260
|
+
/**
|
|
261
|
+
* Callback triggered when the element leaves the viewport.
|
|
262
|
+
*/
|
|
263
|
+
onLeave?: (entry: IntersectionObserverEntry, direction: ViewportDirection) => void;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
declare const ViewportPlugin: Plugin_2;
|
|
267
|
+
export { ViewportPlugin }
|
|
268
|
+
export default ViewportPlugin;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Options for the ViewportPlugin installation.
|
|
272
|
+
*/
|
|
273
|
+
export declare interface ViewportPluginOptions {
|
|
274
|
+
/**
|
|
275
|
+
* Custom presets to merge with or override the default presets.
|
|
276
|
+
* Key is the preset name, value is the CSS class(es).
|
|
277
|
+
*/
|
|
278
|
+
presets?: Record<string, string>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Vue Directive: v-viewport
|
|
283
|
+
*
|
|
284
|
+
* Detects when an element enters the viewport and applies animation states via data attributes.
|
|
285
|
+
* Uses data-vp-* attributes to avoid conflicts with Vue's class binding.
|
|
286
|
+
*/
|
|
287
|
+
export declare const vViewport: Directive<HTMLElement, ViewportOptions | string>;
|
|
288
|
+
|
|
289
|
+
export { }
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
declare module 'vue' {
|
|
293
|
+
interface HTMLAttributes {
|
|
294
|
+
onViewEnter?: (event: ViewportEvent) => void;
|
|
295
|
+
onViewLeave?: (event: ViewportEvent) => void;
|
|
296
|
+
}
|
|
297
|
+
interface GlobalDirectives {
|
|
298
|
+
vViewport: ViewportOptions | string;
|
|
299
|
+
}
|
|
300
|
+
}
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--viewport-duration: .6s;--viewport-ease: ease-out;--viewport-delay: 0s;--viewport-distance: 2rem;--viewport-stagger: .1s;--viewport-scale-in: .95;--viewport-scale-out: 1.05;--viewport-blur: 12px}[data-vp-preset*=animate-]{transition-duration:var(--viewport-duration);transition-timing-function:var(--viewport-ease);transition-delay:var(--viewport-delay)}[data-vp-preset~=animate-fade],[data-vp-preset~=animate-fade-down][data-vp-entry=down],[data-vp-preset~=animate-fade-down][data-vp-pos=below],[data-vp-preset~=animate-fade-up][data-vp-entry=up],[data-vp-preset~=animate-fade-up][data-vp-pos=above]{opacity:0;transition-property:opacity}[data-vp-preset~=animate-fade][data-vp-in-view],[data-vp-preset~=animate-fade-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-fade-up][data-vp-entry=up][data-vp-in-view]{opacity:1}[data-vp-preset~=animate-fade-up],[data-vp-preset~=animate-fade-up-down][data-vp-entry=down],[data-vp-preset~=animate-fade-up-down][data-vp-pos=below],[data-vp-preset~=animate-fade-up-up][data-vp-entry=up],[data-vp-preset~=animate-fade-up-up][data-vp-pos=above]{opacity:0;transform:translateY(var(--viewport-distance));transition-property:opacity,transform}[data-vp-preset~=animate-fade-up][data-vp-in-view],[data-vp-preset~=animate-fade-up-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-fade-up-up][data-vp-entry=up][data-vp-in-view]{opacity:1;transform:translateY(0)}[data-vp-preset~=animate-fade-down],[data-vp-preset~=animate-fade-down-down][data-vp-entry=down],[data-vp-preset~=animate-fade-down-down][data-vp-pos=below],[data-vp-preset~=animate-fade-down-up][data-vp-entry=up],[data-vp-preset~=animate-fade-down-up][data-vp-pos=above]{opacity:0;transform:translateY(calc(var(--viewport-distance) * -1));transition-property:opacity,transform}[data-vp-preset~=animate-fade-down][data-vp-in-view],[data-vp-preset~=animate-fade-down-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-fade-down-up][data-vp-entry=up][data-vp-in-view]{opacity:1;transform:translateY(0)}[data-vp-preset~=animate-fade-left],[data-vp-preset~=animate-fade-left-down][data-vp-entry=down],[data-vp-preset~=animate-fade-left-down][data-vp-pos=below],[data-vp-preset~=animate-fade-left-up][data-vp-entry=up],[data-vp-preset~=animate-fade-left-up][data-vp-pos=above]{opacity:0;transform:translate(var(--viewport-distance));transition-property:opacity,transform}[data-vp-preset~=animate-fade-left][data-vp-in-view],[data-vp-preset~=animate-fade-left-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-fade-left-up][data-vp-entry=up][data-vp-in-view]{opacity:1;transform:translate(0)}[data-vp-preset~=animate-fade-right],[data-vp-preset~=animate-fade-right-down][data-vp-entry=down],[data-vp-preset~=animate-fade-right-down][data-vp-pos=below],[data-vp-preset~=animate-fade-right-up][data-vp-entry=up],[data-vp-preset~=animate-fade-right-up][data-vp-pos=above]{opacity:0;transform:translate(calc(var(--viewport-distance) * -1));transition-property:opacity,transform}[data-vp-preset~=animate-fade-right][data-vp-in-view],[data-vp-preset~=animate-fade-right-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-fade-right-up][data-vp-entry=up][data-vp-in-view]{opacity:1;transform:translate(0)}[data-vp-preset~=animate-slide-up],[data-vp-preset~=animate-slide-up-down][data-vp-entry=down],[data-vp-preset~=animate-slide-up-down][data-vp-pos=below],[data-vp-preset~=animate-slide-up-up][data-vp-entry=up],[data-vp-preset~=animate-slide-up-up][data-vp-pos=above]{transform:translateY(var(--viewport-distance));transition-property:transform}[data-vp-preset~=animate-slide-up][data-vp-in-view],[data-vp-preset~=animate-slide-up-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-slide-up-up][data-vp-entry=up][data-vp-in-view]{transform:translateY(0)}[data-vp-preset~=animate-slide-down],[data-vp-preset~=animate-slide-down-down][data-vp-entry=down],[data-vp-preset~=animate-slide-down-down][data-vp-pos=below],[data-vp-preset~=animate-slide-down-up][data-vp-entry=up],[data-vp-preset~=animate-slide-down-up][data-vp-pos=above]{transform:translateY(calc(var(--viewport-distance) * -1));transition-property:transform}[data-vp-preset~=animate-slide-down][data-vp-in-view],[data-vp-preset~=animate-slide-down-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-slide-down-up][data-vp-entry=up][data-vp-in-view]{transform:translateY(0)}[data-vp-preset~=animate-slide-left],[data-vp-preset~=animate-slide-left-down][data-vp-entry=down],[data-vp-preset~=animate-slide-left-down][data-vp-pos=below],[data-vp-preset~=animate-slide-left-up][data-vp-entry=up],[data-vp-preset~=animate-slide-left-up][data-vp-pos=above]{transform:translate(var(--viewport-distance));transition-property:transform}[data-vp-preset~=animate-slide-left][data-vp-in-view],[data-vp-preset~=animate-slide-left-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-slide-left-up][data-vp-entry=up][data-vp-in-view]{transform:translate(0)}[data-vp-preset~=animate-slide-right],[data-vp-preset~=animate-slide-right-down][data-vp-entry=down],[data-vp-preset~=animate-slide-right-down][data-vp-pos=below],[data-vp-preset~=animate-slide-right-up][data-vp-entry=up],[data-vp-preset~=animate-slide-right-up][data-vp-pos=above]{transform:translate(calc(var(--viewport-distance) * -1));transition-property:transform}[data-vp-preset~=animate-slide-right][data-vp-in-view],[data-vp-preset~=animate-slide-right-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-slide-right-up][data-vp-entry=up][data-vp-in-view]{transform:translate(0)}[data-vp-preset~=animate-scale-up],[data-vp-preset~=animate-scale-up-down][data-vp-entry=down],[data-vp-preset~=animate-scale-up-down][data-vp-pos=below],[data-vp-preset~=animate-scale-up-up][data-vp-entry=up],[data-vp-preset~=animate-scale-up-up][data-vp-pos=above]{opacity:0;transform:scale(var(--viewport-scale-in));transition-property:opacity,transform}[data-vp-preset~=animate-scale-up][data-vp-in-view],[data-vp-preset~=animate-scale-up-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-scale-up-up][data-vp-entry=up][data-vp-in-view]{opacity:1;transform:scale(1)}[data-vp-preset~=animate-scale-down],[data-vp-preset~=animate-scale-down-down][data-vp-entry=down],[data-vp-preset~=animate-scale-down-down][data-vp-pos=below],[data-vp-preset~=animate-scale-down-up][data-vp-entry=up],[data-vp-preset~=animate-scale-down-up][data-vp-pos=above]{opacity:0;transform:scale(var(--viewport-scale-out));transition-property:opacity,transform}[data-vp-preset~=animate-scale-down][data-vp-in-view],[data-vp-preset~=animate-scale-down-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-scale-down-up][data-vp-entry=up][data-vp-in-view]{opacity:1;transform:scale(1)}[data-vp-preset~=animate-blur-in],[data-vp-preset~=animate-blur-in-down][data-vp-entry=down],[data-vp-preset~=animate-blur-in-down][data-vp-pos=below],[data-vp-preset~=animate-blur-in-up][data-vp-entry=up],[data-vp-preset~=animate-blur-in-up][data-vp-pos=above]{opacity:0;filter:blur(var(--viewport-blur));transition-property:opacity,filter}[data-vp-preset~=animate-blur-in][data-vp-in-view],[data-vp-preset~=animate-blur-in-down][data-vp-entry=down][data-vp-in-view],[data-vp-preset~=animate-blur-in-up][data-vp-entry=up][data-vp-in-view]{opacity:1;filter:blur(0)}[data-vp-preset~=animate-fade-y][data-vp-entry=down],[data-vp-preset~=animate-fade-y][data-vp-pos=below],[data-vp-preset~=animate-fade-y][data-vp-entry=up],[data-vp-preset~=animate-fade-y][data-vp-pos=above]{opacity:0;transition-property:opacity,transform}[data-vp-preset~=animate-fade-y][data-vp-entry=down],[data-vp-preset~=animate-fade-y][data-vp-pos=below]{transform:translateY(var(--viewport-distance))}[data-vp-preset~=animate-fade-y][data-vp-entry=up],[data-vp-preset~=animate-fade-y][data-vp-pos=above]{transform:translateY(calc(var(--viewport-distance) * -1))}[data-vp-preset~=animate-fade-y][data-vp-in-view]{opacity:1;transform:translateY(0)}[data-vp-preset~=animate-slide-y][data-vp-entry=down],[data-vp-preset~=animate-slide-y][data-vp-pos=below],[data-vp-preset~=animate-slide-y][data-vp-entry=up],[data-vp-preset~=animate-slide-y][data-vp-pos=above]{transition-property:transform}[data-vp-preset~=animate-slide-y][data-vp-entry=down],[data-vp-preset~=animate-slide-y][data-vp-pos=below]{transform:translateY(var(--viewport-distance))}[data-vp-preset~=animate-slide-y][data-vp-entry=up],[data-vp-preset~=animate-slide-y][data-vp-pos=above]{transform:translateY(calc(var(--viewport-distance) * -1))}[data-vp-preset~=animate-slide-y][data-vp-in-view]{transform:translateY(0)}@media(prefers-reduced-motion:reduce),print{[data-vp-mounted]{opacity:1!important;transform:none!important;transition:none!important;filter:none!important;animation:none!important;translate:none!important}}
|