vendor: 12,596 bytes, lines 1-410
1var VanillaTilt = (function () { 2'use strict'; 3 4/** 5 * Created by Sergiu Èandor (micku7zu) on 1/27/2017. 6 * Original idea: https://github.com/gijsroge/tilt.js 7 * MIT License. 8 * Version 1.8.1 9 */ 10 11class VanillaTilt { 12 constructor(element, settings = {}) { 13 if (!(element instanceof Node)) { 14 throw ("Can't initialize VanillaTilt because " + element + " is not a Node."); 15 } 16 17 this.width = null; 18 this.height = null; 19 this.clientWidth = null; 20 this.clientHeight = null; 21 this.left = null; 22 this.top = null; 23 24 // for Gyroscope sampling 25 this.gammazero = null; 26 this.betazero = null; 27 this.lastgammazero = null; 28 this.lastbetazero = null; 29 30 this.transitionTimeout = null; 31 this.updateCall = null; 32 this.event = null; 33 34 this.updateBind = this.update.bind(this); 35 this.resetBind = this.reset.bind(this); 36 37 this.element = element; 38 this.settings = this.extendSettings(settings); 39 40 this.reverse = this.settings.reverse ? -1 : 1; 41 this.resetToStart = VanillaTilt.isSettingTrue(this.settings["reset-to-start"]); 42 this.glare = VanillaTilt.isSettingTrue(this.settings.glare); 43 this.glarePrerender = VanillaTilt.isSettingTrue(this.settings["glare-prerender"]); 44 this.fullPageListening = VanillaTilt.isSettingTrue(this.settings["full-page-listening"]); 45 this.gyroscope = VanillaTilt.isSettingTrue(this.settings.gyroscope); 46 this.gyroscopeSamples = this.settings.gyroscopeSamples; 47 48 this.elementListener = this.getElementListener(); 49 50 if (this.glare) { 51 this.prepareGlare(); 52 } 53 54 if (this.fullPageListening) { 55 this.updateClientSize(); 56 } 57 58 this.addEventListeners(); 59 this.reset(); 60 61 if (this.resetToStart === false) { 62 this.settings.startX = 0; 63 this.settings.startY = 0; 64 } 65 } 66 67 static isSettingTrue(setting) { 68 return setting === "" || setting === true || setting === 1; 69 } 70 71 /** 72 * Method returns element what will be listen mouse events 73 * @return {Node} 74 */ 75 getElementListener() { 76 if (this.fullPageListening) { 77 return window.document; 78 } 79 80 if (typeof this.settings["mouse-event-element"] === "string") { 81 const mouseEventElement = document.querySelector(this.settings["mouse-event-element"]); 82 83 if (mouseEventElement) { 84 return mouseEventElement; 85 } 86 } 87 88 if (this.settings["mouse-event-element"] instanceof Node) { 89 return this.settings["mouse-event-element"]; 90 } 91 92 return this.element; 93 } 94 95 /** 96 * Method set listen methods for this.elementListener 97 * @return {Node} 98 */ 99 addEventListeners() { 100 this.onMouseEnterBind = this.onMouseEnter.bind(this); 101 this.onMouseMoveBind = this.onMouseMove.bind(this); 102 this.onMouseLeaveBind = this.onMouseLeave.bind(this); 103 this.onWindowResizeBind = this.onWindowResize.bind(this); 104 this.onDeviceOrientationBind = this.onDeviceOrientation.bind(this); 105 106 this.elementListener.addEventListener("mouseenter", this.onMouseEnterBind); 107 this.elementListener.addEventListener("mouseleave", this.onMouseLeaveBind); 108 this.elementListener.addEventListener("mousemove", this.onMouseMoveBind); 109 110 if (this.glare || this.fullPageListening) { 111 window.addEventListener("resize", this.onWindowResizeBind); 112 } 113 114 if (this.gyroscope) { 115 window.addEventListener("deviceorientation", this.onDeviceOrientationBind); 116 } 117 } 118 119 /** 120 * Method remove event listeners from current this.elementListener 121 */ 122 removeEventListeners() { 123 this.elementListener.removeEventListener("mouseenter", this.onMouseEnterBind); 124 this.elementListener.removeEventListener("mouseleave", this.onMouseLeaveBind); 125 this.elementListener.removeEventListener("mousemove", this.onMouseMoveBind); 126 127 if (this.gyroscope) { 128 window.removeEventListener("deviceorientation", this.onDeviceOrientationBind); 129 } 130 131 if (this.glare || this.fullPageListening) { 132 window.removeEventListener("resize", this.onWindowResizeBind); 133 } 134 } 135 136 destroy() { 137 clearTimeout(this.transitionTimeout); 138 if (this.updateCall !== null) { 139 cancelAnimationFrame(this.updateCall); 140 } 141 142 this.element.style.willChange = ""; 143 this.element.style.transition = ""; 144 this.element.style.transform = ""; 145 this.resetGlare(); 146 147 this.removeEventListeners(); 148 this.element.vanillaTilt = null; 149 delete this.element.vanillaTilt; 150 151 this.element = null; 152 } 153 154 onDeviceOrientation(event) { 155 if (event.gamma === null || event.beta === null) { 156 return; 157 } 158 159 this.updateElementPosition(); 160 161 if (this.gyroscopeSamples > 0) { 162 this.lastgammazero = this.gammazero; 163 this.lastbetazero = this.betazero; 164 165 if (this.gammazero === null) { 166 this.gammazero = event.gamma; 167 this.betazero = event.beta; 168 } else { 169 this.gammazero = (event.gamma + this.lastgammazero) / 2; 170 this.betazero = (event.beta + this.lastbetazero) / 2; 171 } 172 173 this.gyroscopeSamples -= 1; 174 } 175 176 const totalAngleX = this.settings.gyroscopeMaxAngleX - this.settings.gyroscopeMinAngleX; 177 const totalAngleY = this.settings.gyroscopeMaxAngleY - this.settings.gyroscopeMinAngleY; 178 179 const degreesPerPixelX = totalAngleX / this.width; 180 const degreesPerPixelY = totalAngleY / this.height; 181 182 const angleX = event.gamma - (this.settings.gyroscopeMinAngleX + this.gammazero); 183 const angleY = event.beta - (this.settings.gyroscopeMinAngleY + this.betazero); 184 185 const posX = angleX / degreesPerPixelX; 186 const posY = angleY / degreesPerPixelY; 187 188 if (this.updateCall !== null) { 189 cancelAnimationFrame(this.updateCall); 190 } 191 192 this.event = { 193 clientX: posX + this.left, 194 clientY: posY + this.top, 195 }; 196 197 this.updateCall = requestAnimationFrame(this.updateBind); 198 } 199 200 onMouseEnter() { 201 this.updateElementPosition(); 202 this.element.style.willChange = "transform"; 203 this.setTransition(); 204 } 205 206 onMouseMove(event) { 207 if (this.updateCall !== null) { 208 cancelAnimationFrame(this.updateCall); 209 } 210 211 this.event = event; 212 this.updateCall = requestAnimationFrame(this.updateBind); 213 } 214 215 onMouseLeave() { 216 this.setTransition(); 217 218 if (this.settings.reset) { 219 requestAnimationFrame(this.resetBind); 220 } 221 } 222 223 reset() { 224 this.onMouseEnter(); 225 226 if (this.fullPageListening) { 227 this.event = { 228 clientX: (this.settings.startX + this.settings.max) / (2 * this.settings.max) * this.clientWidth, 229 clientY: (this.settings.startY + this.settings.max) / (2 * this.settings.max) * this.clientHeight 230 }; 231 } else { 232 this.event = { 233 clientX: this.left + ((this.settings.startX + this.settings.max) / (2 * this.settings.max) * this.width), 234 clientY: this.top + ((this.settings.startY + this.settings.max) / (2 * this.settings.max) * this.height) 235 }; 236 } 237 238 let backupScale = this.settings.scale; 239 this.settings.scale = 1; 240 this.update(); 241 this.settings.scale = backupScale; 242 this.resetGlare(); 243 } 244 245 resetGlare() { 246 if (this.glare) { 247 this.glareElement.style.transform = "rotate(180deg) translate(-50%, -50%)"; 248 this.glareElement.style.opacity = "0"; 249 } 250 } 251 252 getValues() { 253 let x, y; 254 255 if (this.fullPageListening) { 256 x = this.event.clientX / this.clientWidth; 257 y = this.event.clientY / this.clientHeight; 258 } else { 259 x = (this.event.clientX - this.left) / this.width; 260 y = (this.event.clientY - this.top) / this.height; 261 } 262 263 x = Math.min(Math.max(x, 0), 1); 264 y = Math.min(Math.max(y, 0), 1); 265 266 let tiltX = (this.reverse * (this.settings.max - x * this.settings.max * 2)).toFixed(2); 267 let tiltY = (this.reverse * (y * this.settings.max * 2 - this.settings.max)).toFixed(2); 268 let angle = Math.atan2(this.event.clientX - (this.left + this.width / 2), -(this.event.clientY - (this.top + this.height / 2))) * (180 / Math.PI); 269 270 return { 271 tiltX: tiltX, 272 tiltY: tiltY, 273 percentageX: x * 100, 274 percentageY: y * 100, 275 angle: angle 276 }; 277 } 278 279 updateElementPosition() { 280 let rect = this.element.getBoundingClientRect(); 281 282 this.width = this.element.offsetWidth; 283 this.height = this.element.offsetHeight; 284 this.left = rect.left; 285 this.top = rect.top; 286 } 287 288 update() { 289 let values = this.getValues(); 290 291 this.element.style.transform = "perspective(" + this.settings.perspective + "px) " + 292 "rotateX(" + (this.settings.axis === "x" ? 0 : values.tiltY) + "deg) " + 293 "rotateY(" + (this.settings.axis === "y" ? 0 : values.tiltX) + "deg) " + 294 "scale3d(" + this.settings.scale + ", " + this.settings.scale + ", " + this.settings.scale + ")"; 295 296 if (this.glare) { 297 this.glareElement.style.transform = `rotate(${values.angle}deg) translate(-50%, -50%)`; 298 this.glareElement.style.opacity = `${values.percentageY * this.settings["max-glare"] / 100}`; 299 } 300 301 this.element.dispatchEvent(new CustomEvent("tiltChange", { 302 "detail": values 303 })); 304 305 this.updateCall = null; 306 } 307 308 /** 309 * Appends the glare element (if glarePrerender equals false) 310 * and sets the default style 311 */ 312 prepareGlare() { 313 // If option pre-render is enabled we assume all html/css is present for an optimal glare effect. 314 if (!this.glarePrerender) { 315 // Create glare element 316 const jsTiltGlare = document.createElement("div"); 317 jsTiltGlare.classList.add("js-tilt-glare"); 318 319 const jsTiltGlareInner = document.createElement("div"); 320 jsTiltGlareInner.classList.add("js-tilt-glare-inner"); 321 322 jsTiltGlare.appendChild(jsTiltGlareInner); 323 this.element.appendChild(jsTiltGlare); 324 } 325 326 this.glareElementWrapper = this.element.querySelector(".js-tilt-glare"); 327 this.glareElement = this.element.querySelector(".js-tilt-glare-inner"); 328 329 if (this.glarePrerender) { 330 return; 331 } 332 333 Object.assign(this.glareElementWrapper.style, { 334 "position": "absolute", 335 "top": "0", 336 "left": "0", 337 "width": "100%", 338 "height": "100%", 339 "overflow": "hidden", 340 "pointer-events": "none", 341 "border-radius": "inherit" 342 }); 343 344 Object.assign(this.glareElement.style, { 345 "position": "absolute", 346 "top": "50%", 347 "left": "50%", 348 "pointer-events": "none", 349 "background-image": `linear-gradient(0deg, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%)`, 350 "transform": "rotate(180deg) translate(-50%, -50%)", 351 "transform-origin": "0% 0%", 352 "opacity": "0" 353 }); 354 355 this.updateGlareSize(); 356 } 357 358 updateGlareSize() { 359 if (this.glare) { 360 const glareSize = (this.element.offsetWidth > this.element.offsetHeight ? this.element.offsetWidth : this.element.offsetHeight) * 2; 361 362 Object.assign(this.glareElement.style, { 363 "width": `${glareSize}px`, 364 "height": `${glareSize}px`, 365 }); 366 } 367 } 368 369 updateClientSize() { 370 this.clientWidth = window.innerWidth 371 || document.documentElement.clientWidth 372 || document.body.clientWidth; 373 374 this.clientHeight = window.innerHeight 375 || document.documentElement.clientHeight 376 || document.body.clientHeight; 377 } 378 379 onWindowResize() { 380 this.updateGlareSize(); 381 this.updateClientSize(); 382 } 383 384 setTransition() { 385 clearTimeout(this.transitionTimeout); 386 this.element.style.transition = this.settings.speed + "ms " + this.settings.easing; 387 if (this.glare) this.glareElement.style.transition = `opacity ${this.settings.speed}ms ${this.settings.easing}`; 388 389 this.transitionTimeout = setTimeout(() => { 390 this.element.style.transition = ""; 391 if (this.glare) { 392 this.glareElement.style.transition = ""; 393 } 394 }, this.settings.speed); 395 396 } 397 398 /** 399 * Method return patched settings of instance 400 * @param {boolean} settings.reverse - reverse the tilt direction 401 * @param {number} settings.max - max tilt rotation (degrees) 402 * @param {startX} settings.startX - the starting tilt on the X axis, in degrees. Default: 0 403 * @param {startY} settings.startY - the starting tilt on the Y axis, in degrees. Default: 0 404 * @param {number} settings.perspective - Transform perspective, the lower the more extreme the tilt gets 405 * @param {string} settings.easing - Easing on enter/exit 406 * @param {number} settings.scale - 2 = 200%, 1.5 = 150%, etc.. 407 * @param {number} settings.speed - Speed of the enter/exit transition 408 * @param {boolean} settings.transition - Set a transition on enter/exit 409 * @param {string|null} settings.axis - What axis should be enabled. Can be "x" or "y" 410 * @param {boolean} settings.glare - if it should have a "glare" effect
411 * @param {number} settings.max-glare - the maximum "glare" opacity (1 = 100%, 0.5 = 50%) 412 * @param {boolean} settings.glare-prerender - false = VanillaTilt creates the glare elements for you, otherwise 413 * @param {boolean} settings.full-page-listening - If true, parallax effect will listen to mouse move events on the whole document, not only the selected element 414 * @param {string|object} settings.mouse-event-element - String selector or link to HTML-element what will be listen mouse events 415 * @param {boolean} settings.reset - false = If the tilt effect has to be reset on exit 416 * @param {boolean} settings.reset-to-start - true = On reset event (mouse leave) will return to initial start angle (if startX or startY is set) 417 * @param {gyroscope} settings.gyroscope - Enable tilting by deviceorientation events 418 * @param {gyroscopeSensitivity} settings.gyroscopeSensitivity - Between 0 and 1 - The angle at which max tilt position is reached. 1 = 90deg, 0.5 = 45deg, etc.. 419 * @param {gyroscopeSamples} settings.gyroscopeSamples - How many gyroscope moves to decide the starting position. 420 */ 421 extendSettings(settings) { 422 let defaultSettings = { 423 reverse: false, 424 max: 15, 425 startX: 0, 426 startY: 0, 427 perspective: 1000, 428 easing: "cubic-bezier(.03,.98,.52,.99)", 429 scale: 1, 430 speed: 300, 431 transition: true, 432 axis: null, 433 glare: false, 434 "max-glare": 1, 435 "glare-prerender": false, 436 "full-page-listening": false, 437 "mouse-event-element": null, 438 reset: true, 439 "reset-to-start": true, 440 gyroscope: true, 441 gyroscopeMinAngleX: -45, 442 gyroscopeMaxAngleX: 45, 443 gyroscopeMinAngleY: -45, 444 gyroscopeMaxAngleY: 45, 445 gyroscopeSamples: 10 446 }; 447 448 let newSettings = {}; 449 for (var property in defaultSettings) { 450 if (property in settings) { 451 newSettings[property] = settings[property]; 452 } else if (this.element.hasAttribute("data-tilt-" + property)) { 453 let attribute = this.element.getAttribute("data-tilt-" + property); 454 try { 455 newSettings[property] = JSON.parse(attribute); 456 } catch (e) { 457 newSettings[property] = attribute; 458 } 459 460 } else { 461 newSettings[property] = defaultSettings[property]; 462 } 463 } 464 465 return newSettings; 466 } 467 468 static init(elements, settings) { 469 if (elements instanceof Node) { 470 elements = [elements]; 471 } 472 473 if (elements instanceof NodeList) { 474 elements = [].slice.call(elements); 475 } 476 477 if (!(elements instanceof Array)) { 478 return; 479 } 480 481 elements.forEach((element) => { 482 if (!("vanillaTilt" in element)) { 483 element.vanillaTilt = new VanillaTilt(element, settings); 484 } 485 }); 486 } 487} 488 489if (typeof document !== "undefined") { 490 /* expose the class to window */ 491 window.VanillaTilt = VanillaTilt; 492 493 /** 494 * Auto load 495 */ 496 VanillaTilt.init(document.querySelectorAll("[data-tilt]")); 497} 498 499return VanillaTilt; 500 501}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.