-
Notifications
You must be signed in to change notification settings - Fork 0
/
vanila-tilt.js
250 lines (250 loc) · 11.6 KB
/
vanila-tilt.js
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
var VanillaTilt = (function () {
'use strict';
class t {
constructor(e, i = {}) {
if (!(e instanceof Node)) throw "Can't initialize VanillaTilt because " + e + ' is not a Node.';
(this.width = null),
(this.height = null),
(this.clientWidth = null),
(this.clientHeight = null),
(this.left = null),
(this.top = null),
(this.gammazero = null),
(this.betazero = null),
(this.lastgammazero = null),
(this.lastbetazero = null),
(this.transitionTimeout = null),
(this.updateCall = null),
(this.event = null),
(this.updateBind = this.update.bind(this)),
(this.resetBind = this.reset.bind(this)),
(this.element = e),
(this.settings = this.extendSettings(i)),
(this.reverse = this.settings.reverse ? -1 : 1),
(this.glare = t.isSettingTrue(this.settings.glare)),
(this.glarePrerender = t.isSettingTrue(this.settings['glare-prerender'])),
(this.fullPageListening = t.isSettingTrue(this.settings['full-page-listening'])),
(this.gyroscope = t.isSettingTrue(this.settings.gyroscope)),
(this.gyroscopeSamples = this.settings.gyroscopeSamples),
(this.elementListener = this.getElementListener()),
this.glare && this.prepareGlare(),
this.fullPageListening && this.updateClientSize(),
this.addEventListeners(),
this.reset(),
this.updateInitialPosition();
}
static isSettingTrue(t) {
return '' === t || !0 === t || 1 === t;
}
getElementListener() {
if (this.fullPageListening) return window.document;
if ('string' == typeof this.settings['mouse-event-element']) {
const t = document.querySelector(this.settings['mouse-event-element']);
if (t) return t;
}
return this.settings['mouse-event-element'] instanceof Node ? this.settings['mouse-event-element'] : this.element;
}
addEventListeners() {
(this.onMouseEnterBind = this.onMouseEnter.bind(this)),
(this.onMouseMoveBind = this.onMouseMove.bind(this)),
(this.onMouseLeaveBind = this.onMouseLeave.bind(this)),
(this.onWindowResizeBind = this.onWindowResize.bind(this)),
(this.onDeviceOrientationBind = this.onDeviceOrientation.bind(this)),
this.elementListener.addEventListener('mouseenter', this.onMouseEnterBind),
this.elementListener.addEventListener('mouseleave', this.onMouseLeaveBind),
this.elementListener.addEventListener('mousemove', this.onMouseMoveBind),
(this.glare || this.fullPageListening) && window.addEventListener('resize', this.onWindowResizeBind),
this.gyroscope && window.addEventListener('deviceorientation', this.onDeviceOrientationBind);
}
removeEventListeners() {
this.elementListener.removeEventListener('mouseenter', this.onMouseEnterBind),
this.elementListener.removeEventListener('mouseleave', this.onMouseLeaveBind),
this.elementListener.removeEventListener('mousemove', this.onMouseMoveBind),
this.gyroscope && window.removeEventListener('deviceorientation', this.onDeviceOrientationBind),
(this.glare || this.fullPageListening) && window.removeEventListener('resize', this.onWindowResizeBind);
}
destroy() {
clearTimeout(this.transitionTimeout),
null !== this.updateCall && cancelAnimationFrame(this.updateCall),
this.reset(),
this.removeEventListeners(),
(this.element.vanillaTilt = null),
delete this.element.vanillaTilt,
(this.element = null);
}
onDeviceOrientation(t) {
if (null === t.gamma || null === t.beta) return;
this.updateElementPosition(),
this.gyroscopeSamples > 0 &&
((this.lastgammazero = this.gammazero),
(this.lastbetazero = this.betazero),
null === this.gammazero ? ((this.gammazero = t.gamma), (this.betazero = t.beta)) : ((this.gammazero = (t.gamma + this.lastgammazero) / 2), (this.betazero = (t.beta + this.lastbetazero) / 2)),
(this.gyroscopeSamples -= 1));
const e = this.settings.gyroscopeMaxAngleX - this.settings.gyroscopeMinAngleX,
i = this.settings.gyroscopeMaxAngleY - this.settings.gyroscopeMinAngleY,
s = e / this.width,
n = i / this.height,
l = (t.gamma - (this.settings.gyroscopeMinAngleX + this.gammazero)) / s,
a = (t.beta - (this.settings.gyroscopeMinAngleY + this.betazero)) / n;
null !== this.updateCall && cancelAnimationFrame(this.updateCall), (this.event = { clientX: l + this.left, clientY: a + this.top }), (this.updateCall = requestAnimationFrame(this.updateBind));
}
onMouseEnter() {
this.updateElementPosition(), (this.element.style.willChange = 'transform'), this.setTransition();
}
onMouseMove(t) {
null !== this.updateCall && cancelAnimationFrame(this.updateCall), (this.event = t), (this.updateCall = requestAnimationFrame(this.updateBind));
}
onMouseLeave() {
this.setTransition(), this.settings.reset && requestAnimationFrame(this.resetBind);
}
reset() {
(this.event = { clientX: this.left + this.width / 2, clientY: this.top + this.height / 2 }),
this.element && this.element.style && (this.element.style.transform = `perspective(${this.settings.perspective}px) ` + 'rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)'),
this.resetGlare();
}
resetGlare() {
this.glare && ((this.glareElement.style.transform = 'rotate(180deg) translate(-50%, -50%)'), (this.glareElement.style.opacity = '0'));
}
updateInitialPosition() {
if (0 === this.settings.startX && 0 === this.settings.startY) return;
this.onMouseEnter(),
this.fullPageListening
? (this.event = { clientX: ((this.settings.startX + this.settings.max) / (2 * this.settings.max)) * this.clientWidth, clientY: ((this.settings.startY + this.settings.max) / (2 * this.settings.max)) * this.clientHeight })
: (this.event = {
clientX: this.left + ((this.settings.startX + this.settings.max) / (2 * this.settings.max)) * this.width,
clientY: this.top + ((this.settings.startY + this.settings.max) / (2 * this.settings.max)) * this.height,
});
let t = this.settings.scale;
(this.settings.scale = 1), this.update(), (this.settings.scale = t), this.resetGlare();
}
getValues() {
let t, e;
return (
this.fullPageListening ? ((t = this.event.clientX / this.clientWidth), (e = this.event.clientY / this.clientHeight)) : ((t = (this.event.clientX - this.left) / this.width), (e = (this.event.clientY - this.top) / this.height)),
(t = Math.min(Math.max(t, 0), 1)),
(e = Math.min(Math.max(e, 0), 1)),
{
tiltX: (this.reverse * (this.settings.max - t * this.settings.max * 2)).toFixed(2),
tiltY: (this.reverse * (e * this.settings.max * 2 - this.settings.max)).toFixed(2),
percentageX: 100 * t,
percentageY: 100 * e,
angle: Math.atan2(this.event.clientX - (this.left + this.width / 2), -(this.event.clientY - (this.top + this.height / 2))) * (180 / Math.PI),
}
);
}
updateElementPosition() {
let t = this.element.getBoundingClientRect();
(this.width = this.element.offsetWidth), (this.height = this.element.offsetHeight), (this.left = t.left), (this.top = t.top);
}
update() {
let t = this.getValues();
(this.element.style.transform =
'perspective(' +
this.settings.perspective +
'px) rotateX(' +
('x' === this.settings.axis ? 0 : t.tiltY) +
'deg) rotateY(' +
('y' === this.settings.axis ? 0 : t.tiltX) +
'deg) scale3d(' +
this.settings.scale +
', ' +
this.settings.scale +
', ' +
this.settings.scale +
')'),
this.glare && ((this.glareElement.style.transform = `rotate(${t.angle}deg) translate(-50%, -50%)`), (this.glareElement.style.opacity = `${(t.percentageY * this.settings['max-glare']) / 100}`)),
this.element.dispatchEvent(new CustomEvent('tiltChange', { detail: t })),
(this.updateCall = null);
}
prepareGlare() {
if (!this.glarePrerender) {
const t = document.createElement('div');
t.classList.add('js-tilt-glare');
const e = document.createElement('div');
e.classList.add('js-tilt-glare-inner'), t.appendChild(e), this.element.appendChild(t);
}
(this.glareElementWrapper = this.element.querySelector('.js-tilt-glare')),
(this.glareElement = this.element.querySelector('.js-tilt-glare-inner')),
this.glarePrerender ||
(Object.assign(this.glareElementWrapper.style, { position: 'absolute', top: '0', left: '0', width: '100%', height: '100%', overflow: 'hidden', 'pointer-events': 'none' }),
Object.assign(this.glareElement.style, {
position: 'absolute',
top: '50%',
left: '50%',
'pointer-events': 'none',
'background-image': 'linear-gradient(0deg, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%)',
transform: 'rotate(180deg) translate(-50%, -50%)',
'transform-origin': '0% 0%',
opacity: '0',
}),
this.updateGlareSize());
}
updateGlareSize() {
if (this.glare) {
const t = 2 * (this.element.offsetWidth > this.element.offsetHeight ? this.element.offsetWidth : this.element.offsetHeight);
Object.assign(this.glareElement.style, { width: `${t}px`, height: `${t}px` });
}
}
updateClientSize() {
(this.clientWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth), (this.clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight);
}
onWindowResize() {
this.updateGlareSize(), this.updateClientSize();
}
setTransition() {
clearTimeout(this.transitionTimeout),
(this.element.style.transition = this.settings.speed + 'ms ' + this.settings.easing),
this.glare && (this.glareElement.style.transition = `opacity ${this.settings.speed}ms ${this.settings.easing}`),
(this.transitionTimeout = setTimeout(() => {
(this.element.style.transition = ''), this.glare && (this.glareElement.style.transition = '');
}, this.settings.speed));
}
extendSettings(t) {
let e = {
reverse: !1,
max: 15,
startX: 0,
startY: 0,
perspective: 1e3,
easing: 'cubic-bezier(.03,.98,.52,.99)',
scale: 1,
speed: 300,
transition: !0,
axis: null,
glare: !1,
'max-glare': 1,
'glare-prerender': !1,
'full-page-listening': !1,
'mouse-event-element': null,
reset: !0,
gyroscope: !0,
gyroscopeMinAngleX: -45,
gyroscopeMaxAngleX: 45,
gyroscopeMinAngleY: -45,
gyroscopeMaxAngleY: 45,
gyroscopeSamples: 10,
},
i = {};
for (var s in e)
if (s in t) i[s] = t[s];
else if (this.element.hasAttribute('data-tilt-' + s)) {
let t = this.element.getAttribute('data-tilt-' + s);
try {
i[s] = JSON.parse(t);
} catch (e) {
i[s] = t;
}
} else i[s] = e[s];
return i;
}
static init(e, i) {
e instanceof Node && (e = [e]),
e instanceof NodeList && (e = [].slice.call(e)),
e instanceof Array &&
e.forEach((e) => {
'vanillaTilt' in e || (e.vanillaTilt = new t(e, i));
});
}
}
return 'undefined' != typeof document && ((window.VanillaTilt = t), t.init(document.querySelectorAll('[data-tilt]'))), t;
})();