-
Notifications
You must be signed in to change notification settings - Fork 155
/
Copy pathintersection-observer.ts
123 lines (115 loc) · 5.75 KB
/
intersection-observer.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
116
117
118
119
120
121
122
123
import { EventHandler, Browser } from '@syncfusion/ej2-base';
import { debounce } from '@syncfusion/ej2-base';
import { SentinelInfo, SentinelType } from '../base/type';
import { InterSection } from '../base/interface';
export type ScrollDirection = 'up' | 'down' | 'right' | 'left';
/**
* InterSectionObserver - class watch whether it enters the viewport.
*
* @hidden
*/
export class InterSectionObserver {
private containerRect: ClientRect;
private movableContainerRect: ClientRect;
private element: HTMLElement;
private movableEle: HTMLElement;
private fromWheel: boolean = false;
private touchMove: boolean = false;
private options: InterSection = {};
public sentinelInfo: SentinelInfo = {
'up': {
check: (rect: ClientRect, info: SentinelType) => {
const top: number = rect.top - this.containerRect.top;
info.entered = top >= 0;
return top + (this.options.pageHeight / 2) >= 0;
},
axis: 'Y'
},
'down': {
check: (rect: ClientRect, info: SentinelType) => {
const bottom: number = rect.bottom;
info.entered = rect.bottom <= this.containerRect.bottom;
return ((bottom - this.containerRect.top) - (this.options.pageHeight / 2)) <= this.options.pageHeight / 2;
}, axis: 'Y'
},
'right': {
check: (rect: ClientRect, info: SentinelType) => {
const right: number = rect.right;
if (this.movableEle) {
info.entered = right < this.movableContainerRect.right;
return right - this.movableContainerRect.width <= this.movableContainerRect.right;
}
info.entered = right < this.containerRect.right;
return right - this.containerRect.width <= this.containerRect.right;
}, axis: 'X'
},
'left': {
check: (rect: ClientRect, info: SentinelType) => {
const left: number = rect.left;
info.entered = left > 0;
if (this.movableEle) {
return left + this.movableContainerRect.width >= this.movableContainerRect.left;
}
return left + this.containerRect.width >= this.containerRect.left;
}, axis: 'X'
}
};
constructor(element: HTMLElement, options: InterSection, movableEle?: HTMLElement) {
this.element = element;
this.options = options;
this.movableEle = movableEle;
}
public observe(callback: Function, onEnterCallback: Function): void {
this.containerRect = this.options.container.getBoundingClientRect();
EventHandler.add(this.options.container, 'wheel', () => this.fromWheel = true, this);
EventHandler.add(this.options.container, 'scroll', this.virtualScrollHandler(callback, onEnterCallback), this);
if (this.options.movableContainer) {
this.movableContainerRect = this.options.movableContainer.getBoundingClientRect();
EventHandler.add(this.options.scrollbar, 'wheel', () => this.fromWheel = true, this);
EventHandler.add(this.options.scrollbar, 'scroll', this.virtualScrollHandler(callback, onEnterCallback), this);
}
}
public check(direction: ScrollDirection): boolean {
const info: SentinelType = this.sentinelInfo[direction];
if (this.movableContainerRect && (direction === 'left' || direction === 'right')) {
return info.check(this.movableEle.getBoundingClientRect(), info);
}
return info.check(this.element.getBoundingClientRect(), info);
}
private virtualScrollHandler(callback: Function, onEnterCallback: Function): Function {
const delay: number = Browser.info.name === 'chrome' ? 200 : 100;
const debounced100: Function = debounce(callback, delay);
const debounced50: Function = debounce(callback, 50);
this.options.prevTop = this.options.prevLeft = 0;
return (e: Event) => {
const top: number = this.options.movableContainer ? this.options.container.scrollTop : (<HTMLElement>e.target).scrollTop;
const left: number = this.options.movableContainer ? this.options.scrollbar.scrollLeft : (<HTMLElement>e.target).scrollLeft;
let direction: ScrollDirection = this.options.prevTop < top ? 'down' : 'up';
direction = this.options.prevLeft === left ? direction : this.options.prevLeft < left ? 'right' : 'left';
this.options.prevTop = top; this.options.prevLeft = left;
const current: SentinelType = this.sentinelInfo[direction];
if (this.options.axes.indexOf(current.axis) === -1) {
return;
}
const check: boolean = this.check(direction);
if (current.entered) {
if (this.movableEle && (direction === 'right' || direction === 'left')) {
onEnterCallback(this.movableEle, current, direction, { top: top, left: left }, this.fromWheel, check);
} else {
onEnterCallback(this.element, current, direction, { top: top, left: left }, this.fromWheel, check);
}
}
if (check) {
let fn: Function = debounced100;
//this.fromWheel ? this.options.debounceEvent ? debounced100 : callback : debounced100;
if (current.axis === 'X') { fn = debounced50; }
fn({ direction: direction, sentinel: current, offset: { top: top, left: left },
focusElement: document.activeElement});
}
this.fromWheel = false;
};
}
public setPageHeight(value: number): void {
this.options.pageHeight = value;
}
}