@castlenine/svelte-aoe 1.4.0 → 2.0.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright (c) 2024 Alexandre "Castlenine"
3
+ Copyright (c) 2024–2026 Alex "Castlenine"
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  # `@castlenine/svelte-aoe`
4
4
 
5
- [![npm.badge]][npm] [![download.badge]][download]
5
+ [![npm.badge]][npm] [![download.badge]][download] [![contribution.badge]][contribution]
6
6
 
7
- A Svelte component to animate elements, without dependencies
7
+ A Svelte component to animate elements, with no dependencies
8
8
  </div>
9
9
 
10
10
  `@castlenine/svelte-aoe` utilizes the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) to detect when an element enters the viewport.
@@ -17,31 +17,13 @@ When the element is detected as being in the viewport, `@castlenine/svelte-aoe`
17
17
 
18
18
  Use your package manager to install:
19
19
 
20
- ```bash
21
- npm i @castlenine/svelte-aoe --save-dev
20
+ ```shell
21
+ npm i @castlenine/svelte-aoe
22
22
  ```
23
23
 
24
24
  ## Setup
25
25
 
26
- - Import the package
27
-
28
- ```svelte
29
- import AnimateOnEnter from '@castlenine/svelte-aoe';
30
- ```
31
-
32
- - Add the component to your layout/page/component.
33
-
34
- ```svelte
35
- <AnimateOnEnter />
36
- ```
37
-
38
- - Add a `data-aoe` attribute to the element that you want to animate and define an animation.
39
-
40
- ```svelte
41
- <img data-aoe="fade-up" src="https://dummyimage.com/500x300"/>
42
- ```
43
-
44
- ## Example: SvelteKit Global Setup
26
+ 1. Import and add the component **once** in your root layout:
45
27
 
46
28
  File: `src/routes/+layout.svelte`
47
29
 
@@ -55,6 +37,14 @@ File: `src/routes/+layout.svelte`
55
37
  <slot />
56
38
  ```
57
39
 
40
+ 2. Add a `data-aoe` attribute to any element you want to animate:
41
+
42
+ ```svelte
43
+ <img data-aoe="fade-up" src="https://dummyimage.com/500x300"/>
44
+ ```
45
+
46
+ Elements are automatically detected, including those added dynamically by page navigation.
47
+
58
48
  ## Animations
59
49
 
60
50
  ### Normal speed
@@ -95,7 +85,7 @@ File: `src/routes/+layout.svelte`
95
85
 
96
86
  ### Normal speed partial fade
97
87
 
98
- Start wih `0.25` opacity
88
+ Start with `0.25` opacity
99
89
 
100
90
  - `partial-fade`
101
91
  - `partial-fade-up`
@@ -105,7 +95,7 @@ Start wih `0.25` opacity
105
95
 
106
96
  ### Fast speed partial fade
107
97
 
108
- Start wih `0.25` opacity
98
+ Start with `0.25` opacity
109
99
 
110
100
  - `partial-fade-fast`
111
101
  - `partial-fade-up-fast`
@@ -115,7 +105,7 @@ Start wih `0.25` opacity
115
105
 
116
106
  ### Slow speed partial fade
117
107
 
118
- Start wih `0.25` opacity
108
+ Start with `0.25` opacity
119
109
 
120
110
  - `partial-fade-slow`
121
111
  - `partial-fade-up-slow`
@@ -153,6 +143,35 @@ You can add your own animations by following the same pattern in your CSS.
153
143
 
154
144
  `threshold` is either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. A value of `0.0` or `0` indicates that even a single pixel of the target is visible. A value of `1.0` or `1` indicates that the target is completely visible. Defaults to `0.3` (30%).
155
145
 
146
+ ## Per-Element Overrides
147
+
148
+ Individual elements can override the global properties using `data-aoe-*` attributes. If an attribute is not set, the value from `<AnimateOnEnter />` is used, which itself falls back to the package default.
149
+
150
+ | Attribute | Type | Overrides |
151
+ | -------------------- | ---------------------------------------------- | ------------ |
152
+ | `data-aoe-root` | CSS selector `string` | `root` |
153
+ | `data-aoe-root-margin` | `string` in pixel (`px`) or percentage (`%`) | `rootMargin` |
154
+ | `data-aoe-threshold` | `number` between `0` and `1.0` | `threshold` |
155
+
156
+ ### Examples
157
+
158
+ ```svelte
159
+ <!-- Uses global defaults -->
160
+ <img data-aoe="fade-up" src="..." />
161
+
162
+ <!-- Overrides threshold only (animates as soon as 1px is visible) -->
163
+ <img data-aoe="fade-up" data-aoe-threshold="0" src="..." />
164
+
165
+ <!-- Overrides rootMargin only (triggers 200px before entering viewport) -->
166
+ <img data-aoe="fade-up" data-aoe-root-margin="200px" src="..." />
167
+
168
+ <!-- Overrides root to a specific scroll container -->
169
+ <img data-aoe="fade-up" data-aoe-root="#scroll-container" src="..." />
170
+
171
+ <!-- Multiple overrides -->
172
+ <img data-aoe="fade-up" data-aoe-threshold="0.8" data-aoe-root-margin="50px" src="..." />
173
+ ```
174
+
156
175
  ---
157
176
 
158
177
  Inspired by [Animate on Scroll](https://michalsnik.github.io/aos/) and forked from [lliamscholtz/svelte-aoe](https://github.com/lliamscholtz/svelte-aoe)
@@ -161,3 +180,5 @@ Inspired by [Animate on Scroll](https://michalsnik.github.io/aos/) and forked fr
161
180
  [npm.badge]: https://img.shields.io/npm/v/@castlenine/svelte-aoe
162
181
  [download]: https://www.npmjs.com/package/@castlenine/svelte-aoe
163
182
  [download.badge]: https://img.shields.io/npm/d18m/@castlenine/svelte-aoe
183
+ [contribution]: https://github.com/Castlenine/svelte-aoe
184
+ [contribution.badge]: https://img.shields.io/badge/contributions-welcome-green
@@ -1,565 +1,81 @@
1
1
  /* **************************************************** */
2
- /* [direction] */
2
+ /* Base: shared transitions and configurable defaults */
3
3
 
4
- /* normal speed */
5
- [data-aoe='up'] {
6
- transform: translateY(45px);
4
+ [data-aoe] {
5
+ --_aoe-distance: 45px;
6
+ --_aoe-t-transform: 600ms;
7
+ --_aoe-t-opacity: 900ms;
7
8
  transition:
8
- transform 600ms,
9
- opacity 900ms;
10
-
11
- &.aoe {
12
- transform: translateY(0);
13
- transition-delay: 0s;
14
- }
15
- }
16
-
17
- [data-aoe='right'] {
18
- transform: translateX(-45px);
19
- transition:
20
- transform 600ms,
21
- opacity 900ms;
22
-
23
- &.aoe {
24
- transform: translateX(0);
25
- transition-delay: 0s;
26
- }
27
- }
28
-
29
- [data-aoe='down'] {
30
- transform: translateY(-45px);
31
- transition:
32
- transform 600ms,
33
- opacity 900ms;
34
-
35
- &.aoe {
36
- transform: translateY(0);
37
- transition-delay: 0s;
38
- }
39
- }
40
-
41
- [data-aoe='left'] {
42
- transform: translateX(45px);
43
- transition:
44
- transform 600ms,
45
- opacity 900ms;
46
-
47
- &.aoe {
48
- opacity: 1;
49
- transform: translateX(0);
50
- transition-delay: 0s;
51
- }
52
- }
53
-
54
- /* fast speed */
55
- [data-aoe='up-fast'] {
56
- transform: translateY(45px);
57
- transition:
58
- transform 370ms,
59
- opacity 555ms;
60
-
61
- &.aoe {
62
- transform: translateY(0);
63
- transition-delay: 0s;
64
- }
65
- }
66
-
67
- [data-aoe='right-fast'] {
68
- transform: translateX(-45px);
69
- transition:
70
- transform 370ms,
71
- opacity 555ms;
72
-
73
- &.aoe {
74
- transform: translateX(0);
75
- transition-delay: 0s;
76
- }
77
- }
78
-
79
- [data-aoe='down-fast'] {
80
- transform: translateY(-45px);
81
- transition:
82
- transform 370ms,
83
- opacity 555ms;
84
-
85
- &.aoe {
86
- transform: translateY(0);
87
- transition-delay: 0s;
88
- }
89
- }
90
-
91
- [data-aoe='left-fast'] {
92
- transform: translateX(45px);
93
- transition:
94
- transform 370ms,
95
- opacity 555ms;
96
-
97
- &.aoe {
98
- opacity: 1;
99
- transform: translateX(0);
100
- transition-delay: 0s;
101
- }
102
- }
103
-
104
- /* slow speed */
105
- [data-aoe='up-slow'] {
106
- transform: translateY(45px);
107
- transition:
108
- transform 975ms,
109
- opacity 1500ms;
110
-
111
- &.aoe {
112
- transform: translateY(0);
113
- transition-delay: 0s;
114
- }
115
- }
116
-
117
- [data-aoe='right-slow'] {
118
- transform: translateX(-45px);
119
- transition:
120
- transform 975ms,
121
- opacity 1500ms;
122
-
123
- &.aoe {
124
- transform: translateX(0);
125
- transition-delay: 0s;
126
- }
127
- }
128
-
129
- [data-aoe='down-slow'] {
130
- transform: translateY(-45px);
131
- transition:
132
- transform 975ms,
133
- opacity 1500ms;
134
-
135
- &.aoe {
136
- transform: translateY(0);
137
- transition-delay: 0s;
138
- }
139
- }
140
-
141
- [data-aoe='left-slow'] {
142
- transform: translateX(45px);
143
- transition:
144
- transform 975ms,
145
- opacity 1500ms;
146
-
147
- &.aoe {
148
- opacity: 1;
149
- transform: translateX(0);
150
- transition-delay: 0s;
151
- }
9
+ transform var(--_aoe-t-transform),
10
+ opacity var(--_aoe-t-opacity);
152
11
  }
153
12
 
154
13
  /* **************************************************** */
155
- /* fade-[direction] */
156
-
157
- /* normal speed */
158
- [data-aoe='fade'] {
159
- opacity: 0;
160
- transition: opacity 900ms;
161
-
162
- &.aoe {
163
- opacity: 1;
164
- transition-delay: 0s;
165
- }
166
- }
167
-
168
- [data-aoe='fade-up'] {
169
- opacity: 0;
170
- transform: translateY(45px);
171
- transition:
172
- transform 600ms,
173
- opacity 900ms;
174
-
175
- &.aoe {
176
- opacity: 1;
177
- transform: translateY(0);
178
- transition-delay: 0s;
179
- }
180
- }
181
-
182
- [data-aoe='fade-right'] {
183
- opacity: 0;
184
- transform: translateX(-45px);
185
- transition:
186
- transform 600ms,
187
- opacity 900ms;
188
-
189
- &.aoe {
190
- opacity: 1;
191
- transform: translateX(0);
192
- transition-delay: 0s;
193
- }
194
- }
195
-
196
- [data-aoe='fade-down'] {
197
- opacity: 0;
198
- transform: translateY(-45px);
199
- transition:
200
- transform 600ms,
201
- opacity 900ms;
202
-
203
- &.aoe {
204
- opacity: 1;
205
- transform: translateY(0);
206
- transition-delay: 0s;
207
- }
208
- }
209
-
210
- [data-aoe='fade-left'] {
211
- opacity: 0;
212
- transform: translateX(45px);
213
- transition:
214
- transform 600ms,
215
- opacity 900ms;
216
-
217
- &.aoe {
218
- opacity: 1;
219
- transform: translateX(0);
220
- transition-delay: 0s;
221
- }
222
- }
223
-
224
- /* fast speed */
225
- [data-aoe='fade-fast'] {
226
- opacity: 0;
227
- transition: opacity 555ms;
228
-
229
- &.aoe {
230
- opacity: 1;
231
- transition-delay: 0s;
232
- }
233
- }
234
-
235
- [data-aoe='fade-up-fast'] {
236
- opacity: 0;
237
- transform: translateY(45px);
238
- transition:
239
- transform 370ms,
240
- opacity 555ms;
241
-
242
- &.aoe {
243
- opacity: 1;
244
- transform: translateY(0);
245
- transition-delay: 0s;
246
- }
247
- }
248
-
249
- [data-aoe='fade-right-fast'] {
250
- opacity: 0;
251
- transform: translateX(-45px);
252
- transition:
253
- transform 370ms,
254
- opacity 555ms;
255
-
256
- &.aoe {
257
- opacity: 1;
258
- transform: translateX(0);
259
- transition-delay: 0s;
260
- }
261
- }
262
-
263
- [data-aoe='fade-down-fast'] {
264
- opacity: 0;
265
- transform: translateY(-45px);
266
- transition:
267
- transform 370ms,
268
- opacity 555ms;
269
-
270
- &.aoe {
271
- opacity: 1;
272
- transform: translateY(0);
273
- transition-delay: 0s;
274
- }
275
- }
276
-
277
- [data-aoe='fade-left-fast'] {
278
- opacity: 0;
279
- transform: translateX(45px);
280
- transition:
281
- transform 370ms,
282
- opacity 555ms;
283
-
284
- &.aoe {
285
- opacity: 1;
286
- transform: translateX(0);
287
- transition-delay: 0s;
288
- }
289
- }
290
-
291
- /* slow speed */
292
- [data-aoe='fade-slow'] {
293
- opacity: 0;
294
- transition: opacity 1500ms;
295
-
296
- &.aoe {
297
- opacity: 1;
298
- transition-delay: 0s;
299
- }
300
- }
301
-
302
- [data-aoe='fade-up-slow'] {
303
- opacity: 0;
304
- transform: translateY(45px);
305
- transition:
306
- transform 975ms,
307
- opacity 1500ms;
308
-
309
- &.aoe {
310
- opacity: 1;
311
- transform: translateY(0);
312
- transition-delay: 0s;
313
- }
314
- }
315
-
316
- [data-aoe='fade-right-slow'] {
317
- opacity: 0;
318
- transform: translateX(-45px);
319
- transition:
320
- transform 975ms,
321
- opacity 1500ms;
322
-
323
- &.aoe {
324
- opacity: 1;
325
- transform: translateX(0);
326
- transition-delay: 0s;
327
- }
328
- }
329
-
330
- [data-aoe='fade-down-slow'] {
331
- opacity: 0;
332
- transform: translateY(-45px);
333
- transition:
334
- transform 975ms,
335
- opacity 1500ms;
14
+ /* Speed modifiers */
336
15
 
337
- &.aoe {
338
- opacity: 1;
339
- transform: translateY(0);
340
- transition-delay: 0s;
341
- }
16
+ [data-aoe$='-fast'] {
17
+ --_aoe-t-transform: 370ms;
18
+ --_aoe-t-opacity: 555ms;
342
19
  }
343
20
 
344
- [data-aoe='fade-left-slow'] {
345
- opacity: 0;
346
- transform: translateX(45px);
347
- transition:
348
- transform 975ms,
349
- opacity 1500ms;
350
-
351
- &.aoe {
352
- opacity: 1;
353
- transform: translateX(0);
354
- transition-delay: 0s;
355
- }
21
+ [data-aoe$='-slow'] {
22
+ --_aoe-t-transform: 975ms;
23
+ --_aoe-t-opacity: 1500ms;
356
24
  }
357
25
 
358
26
  /* **************************************************** */
359
- /* partial-fade-[direction] */
27
+ /* Direction: initial displaced state */
360
28
 
361
- /* normal speed */
362
- [data-aoe='partial-fade'] {
363
- opacity: 0.25;
364
- transition: opacity 900ms;
365
-
366
- &.aoe {
367
- opacity: 1;
368
- transition-delay: 0s;
369
- }
29
+ [data-aoe*='up'] {
30
+ transform: translateY(var(--_aoe-distance));
370
31
  }
371
32
 
372
- [data-aoe='partial-fade-up'] {
373
- opacity: 0.25;
374
- transform: translateY(45px);
375
- transition:
376
- transform 600ms,
377
- opacity 900ms;
378
-
379
- &.aoe {
380
- opacity: 1;
381
- transform: translateY(0);
382
- transition-delay: 0s;
383
- }
33
+ [data-aoe*='right'] {
34
+ transform: translateX(calc(-1 * var(--_aoe-distance)));
384
35
  }
385
36
 
386
- [data-aoe='partial-fade-right'] {
387
- opacity: 0.25;
388
- transform: translateX(-45px);
389
- transition:
390
- transform 600ms,
391
- opacity 900ms;
392
-
393
- &.aoe {
394
- opacity: 1;
395
- transform: translateX(0);
396
- transition-delay: 0s;
397
- }
37
+ [data-aoe*='down'] {
38
+ transform: translateY(calc(-1 * var(--_aoe-distance)));
398
39
  }
399
40
 
400
- [data-aoe='partial-fade-down'] {
401
- opacity: 0.25;
402
- transform: translateY(-45px);
403
- transition:
404
- transform 600ms,
405
- opacity 900ms;
406
-
407
- &.aoe {
408
- opacity: 1;
409
- transform: translateY(0);
410
- transition-delay: 0s;
411
- }
412
- }
413
-
414
- [data-aoe='partial-fade-left'] {
415
- opacity: 0.25;
416
- transform: translateX(45px);
417
- transition:
418
- transform 600ms,
419
- opacity 900ms;
420
-
421
- &.aoe {
422
- opacity: 1;
423
- transform: translateX(0);
424
- transition-delay: 0s;
425
- }
41
+ [data-aoe*='left'] {
42
+ transform: translateX(var(--_aoe-distance));
426
43
  }
427
44
 
428
- /* fast speed */
429
- [data-aoe='partial-fade-fast'] {
430
- opacity: 0.25;
431
- transition: opacity 555ms;
432
-
433
- &.aoe {
434
- opacity: 1;
435
- transition-delay: 0s;
436
- }
437
- }
438
-
439
- [data-aoe='partial-fade-up-fast'] {
440
- opacity: 0.25;
441
- transform: translateY(45px);
442
- transition:
443
- transform 370ms,
444
- opacity 555ms;
445
-
446
- &.aoe {
447
- opacity: 1;
448
- transform: translateY(0);
449
- transition-delay: 0s;
450
- }
451
- }
452
-
453
- [data-aoe='partial-fade-right-fast'] {
454
- opacity: 0.25;
455
- transform: translateX(-45px);
456
- transition:
457
- transform 370ms,
458
- opacity 555ms;
459
-
460
- &.aoe {
461
- opacity: 1;
462
- transform: translateX(0);
463
- transition-delay: 0s;
464
- }
465
- }
466
-
467
- [data-aoe='partial-fade-down-fast'] {
468
- opacity: 0.25;
469
- transform: translateY(-45px);
470
- transition:
471
- transform 370ms,
472
- opacity 555ms;
473
-
474
- &.aoe {
475
- opacity: 1;
476
- transform: translateY(0);
477
- transition-delay: 0s;
478
- }
479
- }
480
-
481
- [data-aoe='partial-fade-left-fast'] {
482
- opacity: 0.25;
483
- transform: translateX(45px);
484
- transition:
485
- transform 370ms,
486
- opacity 555ms;
45
+ /* **************************************************** */
46
+ /* Opacity: initial hidden state */
487
47
 
488
- &.aoe {
489
- opacity: 1;
490
- transform: translateX(0);
491
- transition-delay: 0s;
492
- }
48
+ [data-aoe^='fade'] {
49
+ opacity: 0;
493
50
  }
494
51
 
495
- /* slow speed */
496
- [data-aoe='partial-fade-slow'] {
52
+ [data-aoe^='partial-fade'] {
497
53
  opacity: 0.25;
498
- transition: opacity 1500ms;
499
-
500
- &.aoe {
501
- opacity: 1;
502
- transition-delay: 0s;
503
- }
504
54
  }
505
55
 
506
- [data-aoe='partial-fade-up-slow'] {
507
- opacity: 0.25;
508
- transform: translateY(45px);
509
- transition:
510
- transform 975ms,
511
- opacity 1500ms;
56
+ /* **************************************************** */
57
+ /* Animated state */
512
58
 
513
- &.aoe {
514
- opacity: 1;
515
- transform: translateY(0);
516
- transition-delay: 0s;
517
- }
59
+ [data-aoe].aoe {
60
+ transition-delay: 0s;
518
61
  }
519
62
 
520
- [data-aoe='partial-fade-right-slow'] {
521
- opacity: 0.25;
522
- transform: translateX(-45px);
523
- transition:
524
- transform 975ms,
525
- opacity 1500ms;
526
-
527
- &.aoe {
528
- opacity: 1;
529
- transform: translateX(0);
530
- transition-delay: 0s;
531
- }
63
+ [data-aoe*='up'].aoe,
64
+ [data-aoe*='down'].aoe {
65
+ transform: translateY(0);
532
66
  }
533
67
 
534
- [data-aoe='partial-fade-down-slow'] {
535
- opacity: 0.25;
536
- transform: translateY(-45px);
537
- transition:
538
- transform 975ms,
539
- opacity 1500ms;
540
-
541
- &.aoe {
542
- opacity: 1;
543
- transform: translateY(0);
544
- transition-delay: 0s;
545
- }
68
+ [data-aoe*='right'].aoe,
69
+ [data-aoe*='left'].aoe {
70
+ transform: translateX(0);
546
71
  }
547
72
 
548
- [data-aoe='partial-fade-left-slow'] {
549
- opacity: 0.25;
550
- transform: translateX(45px);
551
- transition:
552
- transform 975ms,
553
- opacity 1500ms;
554
-
555
- &.aoe {
556
- opacity: 1;
557
- transform: translateX(0);
558
- transition-delay: 0s;
559
- }
73
+ [data-aoe^='fade'].aoe,
74
+ [data-aoe^='partial-fade'].aoe {
75
+ opacity: 1;
560
76
  }
561
77
 
562
- /** You can add your own animations by following the same in your own css file
78
+ /** You can add your own animations by following the same pattern in your own CSS file
563
79
  [data-aoe='your-animation'] {
564
80
  transform: translateX(-45px);
565
81
  transition:
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,gBAAgB,CAAC;AAE5C,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,gBAAgB,CAAC;AAE5C,eAAe,cAAc,CAAC"}
package/dist/index.svelte CHANGED
@@ -1,35 +1,106 @@
1
+ <!--
2
+ @component
3
+ ### AnimateOnEnter (AOE)
4
+ This component is used to animate elements when they enter the viewport with the help of [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
5
+
6
+ Place this component once in your root layout. Elements with `data-aoe` attributes will be automatically detected, including dynamically added elements from page navigation.
7
+
8
+ #### Configuration cascade
9
+ 1. Package defaults: `root: null`, `rootMargin: '0px'`, `threshold: 0.3`
10
+ 2. Component props: override package defaults globally
11
+ 3. Per-element `data-aoe-*` attributes: override component props for individual elements
12
+
13
+ &nbsp;
14
+
15
+ @param {Document | Element | null | undefined} `root` - The element that is used as the viewport for checking visibility of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if `null`.
16
+ @param {string | undefined} `rootMargin` - Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages. Defaults to '0px' (no margin).
17
+ @param {number | number[]} `threshold` - Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. A value of 0.0 indicates that even a single pixel of the target is visible. A value of 1.0 indicates that the target is completely visible. Defaults to 0.3 (30%).
18
+ -->
19
+
1
20
  <script>import "./animations.css";
2
21
  import { onMount } from "svelte";
3
22
  export let root = null;
4
23
  export let rootMargin = "0px";
5
24
  export let threshold = 0.3;
6
25
  onMount(() => {
7
- const ELEMENTS_TO_LOAD_IN = /* @__PURE__ */ new Set([...document.querySelectorAll("[data-aoe]")]);
8
- const OBSERVER_OPTIONS = {
9
- root,
10
- rootMargin,
11
- threshold
12
- };
13
- function observerCallback(entries) {
14
- entries.forEach((entry) => {
15
- if (entry.isIntersecting) {
16
- entry.target.classList.add("aoe");
26
+ const DEFAULT_ROOT = root !== null && root !== void 0 ? root : null;
27
+ const DEFAULT_ROOT_MARGIN = rootMargin !== null && rootMargin !== void 0 ? rootMargin : "0px";
28
+ const DEFAULT_THRESHOLD = threshold !== null && threshold !== void 0 ? threshold : 0.3;
29
+ const OBSERVERS = /* @__PURE__ */ new Map();
30
+ const REGISTERED = /* @__PURE__ */ new WeakSet();
31
+ function createObserver(options) {
32
+ const OBSERVER = new IntersectionObserver((entries) => {
33
+ for (const ENTRY of entries) {
34
+ if (ENTRY.isIntersecting) {
35
+ ENTRY.target.classList.add("aoe");
36
+ OBSERVER.unobserve(ENTRY.target);
37
+ }
17
38
  }
39
+ }, options);
40
+ return OBSERVER;
41
+ }
42
+ function configKey(rootSelector, rm, th) {
43
+ const TH_STR = Array.isArray(th) ? th.join(",") : String(th);
44
+ return `${rootSelector !== null && rootSelector !== void 0 ? rootSelector : "null"}|${rm}|${TH_STR}`;
45
+ }
46
+ const DEFAULT_KEY = configKey(null, DEFAULT_ROOT_MARGIN, DEFAULT_THRESHOLD);
47
+ const DEFAULT_OBSERVER = createObserver({
48
+ root: DEFAULT_ROOT,
49
+ rootMargin: DEFAULT_ROOT_MARGIN,
50
+ threshold: DEFAULT_THRESHOLD
51
+ });
52
+ OBSERVERS.set(DEFAULT_KEY, DEFAULT_OBSERVER);
53
+ function getOrCreateObserver(key, options) {
54
+ let observer = OBSERVERS.get(key);
55
+ if (!observer) {
56
+ observer = createObserver(options);
57
+ OBSERVERS.set(key, observer);
58
+ }
59
+ return observer;
60
+ }
61
+ function registerElement(el) {
62
+ if (REGISTERED.has(el) || el.classList.contains("aoe"))
63
+ return;
64
+ REGISTERED.add(el);
65
+ const ROOT_ATTR = el.getAttribute("data-aoe-root");
66
+ const ROOT_MARGIN_ATTR = el.getAttribute("data-aoe-root-margin");
67
+ const THRESHOLD_ATTR = el.getAttribute("data-aoe-threshold");
68
+ if (!ROOT_ATTR && !ROOT_MARGIN_ATTR && !THRESHOLD_ATTR) {
69
+ DEFAULT_OBSERVER.observe(el);
70
+ return;
71
+ }
72
+ const EL_ROOT = ROOT_ATTR ? document.querySelector(ROOT_ATTR) : DEFAULT_ROOT;
73
+ const EL_ROOT_MARGIN = ROOT_MARGIN_ATTR !== null && ROOT_MARGIN_ATTR !== void 0 ? ROOT_MARGIN_ATTR : DEFAULT_ROOT_MARGIN;
74
+ const PARSED_THRESHOLD = THRESHOLD_ATTR != null ? parseFloat(THRESHOLD_ATTR) : null;
75
+ const EL_THRESHOLD = PARSED_THRESHOLD != null && !isNaN(PARSED_THRESHOLD) ? PARSED_THRESHOLD : DEFAULT_THRESHOLD;
76
+ const KEY = configKey(ROOT_ATTR, EL_ROOT_MARGIN, EL_THRESHOLD);
77
+ const OBSERVER = getOrCreateObserver(KEY, {
78
+ root: EL_ROOT,
79
+ rootMargin: EL_ROOT_MARGIN,
80
+ threshold: EL_THRESHOLD
18
81
  });
82
+ OBSERVER.observe(el);
19
83
  }
20
- const OBSERVER = new IntersectionObserver(observerCallback, OBSERVER_OPTIONS);
21
- ELEMENTS_TO_LOAD_IN.forEach((el) => OBSERVER.observe(el));
84
+ document.querySelectorAll("[data-aoe]").forEach((el) => registerElement(el));
85
+ const MUTATION_OBSERVER = new MutationObserver((mutations) => {
86
+ for (const MUTATION of mutations) {
87
+ for (const NODE of MUTATION.addedNodes) {
88
+ if (NODE.nodeType !== Node.ELEMENT_NODE)
89
+ continue;
90
+ const el = NODE;
91
+ if (el.hasAttribute("data-aoe"))
92
+ registerElement(el);
93
+ el.querySelectorAll("[data-aoe]").forEach((child) => registerElement(child));
94
+ }
95
+ }
96
+ });
97
+ MUTATION_OBSERVER.observe(document.body, { childList: true, subtree: true });
98
+ return () => {
99
+ MUTATION_OBSERVER.disconnect();
100
+ for (const OBSERVER of OBSERVERS.values()) {
101
+ OBSERVER.disconnect();
102
+ }
103
+ OBSERVERS.clear();
104
+ };
22
105
  });
23
106
  </script>
24
-
25
- <!--
26
- @component
27
- ### AnimationOnEnter (AOE)
28
- This component is used to animate elements when they enter the viewport with the help of [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
29
-
30
- &nbsp;
31
-
32
- @param {Document | Element | null | undefined} `root` - The element that is used as the viewport for checking visibility of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if null. Defaults to null (browser viewport).
33
- @param {string | undefined} `rootMargin` - Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages. Defaults to '0px' (no margin).
34
- @param {number | number[]} `threshold` - Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. A value of 0.0 indicates that even a single pixel of the target is visible. A value of 1.0 indicates that the target is completely visible. Defaults to 0.3 (30%).
35
- -->
@@ -15,12 +15,19 @@ export type IndexProps = typeof __propDef.props;
15
15
  export type IndexEvents = typeof __propDef.events;
16
16
  export type IndexSlots = typeof __propDef.slots;
17
17
  /**
18
- * ### AnimationOnEnter (AOE)
18
+ * ### AnimateOnEnter (AOE)
19
19
  * This component is used to animate elements when they enter the viewport with the help of [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
20
20
  *
21
+ * Place this component once in your root layout. Elements with `data-aoe` attributes will be automatically detected, including dynamically added elements from page navigation.
22
+ *
23
+ * #### Configuration cascade
24
+ * 1. Package defaults: `root: null`, `rootMargin: '0px'`, `threshold: 0.3`
25
+ * 2. Component props: override package defaults globally
26
+ * 3. Per-element `data-aoe-*` attributes: override component props for individual elements
27
+ *
21
28
  * &nbsp;
22
29
  *
23
- * @param {Document | Element | null | undefined} `root` - The element that is used as the viewport for checking visibility of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if null. Defaults to null (browser viewport).
30
+ * @param {Document | Element | null | undefined} `root` - The element that is used as the viewport for checking visibility of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if `null`.
24
31
  * @param {string | undefined} `rootMargin` - Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages. Defaults to '0px' (no margin).
25
32
  * @param {number | number[]} `threshold` - Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. A value of 0.0 indicates that even a single pixel of the target is visible. A value of 1.0 indicates that the target is completely visible. Defaults to 0.3 (30%).
26
33
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/index.svelte"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAE5C;AACD,OAAO,kBAAkB,CAAC;AAyC1B,QAAA,MAAM,SAAS;;eADyE,QAAQ,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS;qBAAe,MAAM,GAAG,SAAS;oBAAc,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;;;;;;CAClJ,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,OAAO,SAAS,CAAC,KAAK,CAAC;AAChD,MAAM,MAAM,WAAW,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,SAAS,CAAC,KAAK,CAAC;AAEhD;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,oBAAoB,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,CAAC;CAC3F"}
1
+ {"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../src/lib/index.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAE5C;AACD,OAAO,kBAAkB,CAAC;AA8G1B,QAAA,MAAM,SAAS;;eADyE,QAAQ,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS;qBAAe,MAAM,GAAG,SAAS;oBAAc,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;;;;;;CAChJ,CAAC;AAC1D,MAAM,MAAM,UAAU,GAAG,OAAO,SAAS,CAAC,KAAK,CAAC;AAChD,MAAM,MAAM,WAAW,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,SAAS,CAAC,KAAK,CAAC;AAEhD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,oBAAoB,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,CAAC;CAC3F"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@castlenine/svelte-aoe",
3
- "version": "1.4.0",
4
- "description": "A Svelte component to animate elements, without dependencies.",
3
+ "version": "2.0.0",
4
+ "description": "A Svelte component to animate elements, with no dependencies.",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "animate-on-scroll",
@@ -14,7 +14,7 @@
14
14
  "sveltekit"
15
15
  ],
16
16
  "author": {
17
- "name": "Alexandre Castlenine",
17
+ "name": "Alex Castlenine",
18
18
  "url": "https://github.com/Castlenine"
19
19
  },
20
20
  "homepage": "https://github.com/castlenine/svelte-aoe",
@@ -27,12 +27,13 @@
27
27
  },
28
28
  "type": "module",
29
29
  "types": "./dist/index.d.ts",
30
- "import": "./dist/index.js",
31
30
  "svelte": "./dist/index.js",
31
+ "sideEffects": [
32
+ "**/*.css"
33
+ ],
32
34
  "exports": {
33
35
  ".": {
34
36
  "types": "./dist/index.d.ts",
35
- "import": "./dist/index.js",
36
37
  "svelte": "./dist/index.js"
37
38
  }
38
39
  },
@@ -44,7 +45,7 @@
44
45
  "build": "npm run check && vite build",
45
46
  "preview": "vite preview",
46
47
  "package": "npm run remove-dist-folder && svelte-kit sync && svelte-package && publint",
47
- "prepublishOnly": "npm run package",
48
+ "prepublish-only": "npm run package",
48
49
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
49
50
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
50
51
  "eslint": "eslint --ignore-path ./.eslintignore .",
@@ -94,7 +95,7 @@
94
95
  "svelte-eslint-parser": "^0.35.0",
95
96
  "svelte-preprocess": "^5.1.4",
96
97
  "tslib": "^2.6.2",
97
- "typescript": "^5.4.5",
98
+ "typescript": "~4.9.5",
98
99
  "vite": "^4.5.3"
99
100
  }
100
101
  }