-
Notifications
You must be signed in to change notification settings - Fork 23
/
inview.ts
115 lines (100 loc) · 2.96 KB
/
inview.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { tick } from 'svelte';
import type { ActionReturn } from 'svelte/action';
import type {
ObserverEventDetails,
Options,
Position,
ScrollDirection,
Event,
LifecycleEventDetails,
Attributes,
} from './types';
const defaultOptions: Options = {
root: null,
rootMargin: '0px',
threshold: 0,
unobserveOnEnter: false,
};
const createEvent = <T = ObserverEventDetails>(
name: Event,
detail: T
): CustomEvent<T> => new CustomEvent(name, { detail });
export function inview(
node: HTMLElement,
options: Options = {}
): ActionReturn<Options, Attributes> {
const { root, rootMargin, threshold, unobserveOnEnter }: Options = {
...defaultOptions,
...options,
};
let prevPos: Position = {
x: undefined,
y: undefined,
};
let scrollDirection: ScrollDirection = {
vertical: undefined,
horizontal: undefined,
};
if (typeof IntersectionObserver !== 'undefined' && node) {
const observer = new IntersectionObserver(
(entries, _observer) => {
entries.forEach((singleEntry) => {
if (prevPos.y > singleEntry.boundingClientRect.y) {
scrollDirection.vertical = 'up';
} else {
scrollDirection.vertical = 'down';
}
if (prevPos.x > singleEntry.boundingClientRect.x) {
scrollDirection.horizontal = 'left';
} else {
scrollDirection.horizontal = 'right';
}
prevPos = {
y: singleEntry.boundingClientRect.y,
x: singleEntry.boundingClientRect.x,
};
const detail: ObserverEventDetails = {
inView: singleEntry.isIntersecting,
entry: singleEntry,
scrollDirection,
node,
observer: _observer,
};
node.dispatchEvent(createEvent('inview_change', detail));
//@ts-expect-error only for backward compatibility
node.dispatchEvent(createEvent('change', detail));
if (singleEntry.isIntersecting) {
node.dispatchEvent(createEvent('inview_enter', detail));
//@ts-expect-error only for backward compatibility
node.dispatchEvent(createEvent('enter', detail));
unobserveOnEnter && _observer.unobserve(node);
} else {
node.dispatchEvent(createEvent('inview_leave', detail));
//@ts-expect-error only for backward compatibility
node.dispatchEvent(createEvent('leave', detail));
}
});
},
{
root,
rootMargin,
threshold,
}
);
tick().then(() => {
node.dispatchEvent(
createEvent<LifecycleEventDetails>('inview_init', { observer, node })
);
node.dispatchEvent(
//@ts-expect-error only for backward compatibility
createEvent<LifecycleEventDetails>('init', { observer, node })
);
});
observer.observe(node);
return {
destroy() {
observer.unobserve(node);
},
};
}
}