1(() => { 2 const inOutMap = (y = 30) => { 3 return ["elInY+elHeight", `elCenterY-${y}`, "elCenterY", `elCenterY+${y}`, "elOutY-elHeight"] 4 } 5 6 const laxPresets = { 7 fadeInOut: (y = 30, str = 0) => ({ 8 "opacity": [ 9 inOutMap(y), 10 [str, 1, 1, 1, str] 11 ], 12 }), 13 fadeIn: (y = 'elCenterY', str = 0) => ({ 14 "opacity": [ 15 ["elInY+elHeight", y], 16 [str, 1], 17 ], 18 }), 19 fadeOut: (y = 'elCenterY', str = 0) => ({ 20 "opacity": [ 21 [y, "elOutY-elHeight"], 22 [1, str], 23 ], 24 }), 25 blurInOut: (y = 100, str = 20) => ({ 26 "blur": [ 27 inOutMap(y), 28 [str, 0, 0, 0, str], 29 ], 30 }), 31 blurIn: (y = 'elCenterY', str = 20) => ({ 32 "blur": [ 33 ["elInY+elHeight", y], 34 [str, 0], 35 ], 36 }), 37 blurOut: (y = 'elCenterY', str = 20) => ({ 38 "opacity": [ 39 [y, "elOutY-elHeight"], 40 [0, str], 41 ], 42 }), 43 scaleInOut: (y = 100, str = 0.6) => ({ 44 "scale": [ 45 inOutMap(y), 46 [str, 1, 1, 1, str], 47 ], 48 }), 49 scaleIn: (y = 'elCenterY', str = 0.6) => ({ 50 "scale": [ 51 ["elInY+elHeight", y], 52 [str, 1], 53 ], 54 }), 55 scaleOut: (y = 'elCenterY', str = 0.6) => ({ 56 "scale": [ 57 [y, "elOutY-elHeight"], 58 [1, str], 59 ], 60 }), 61 slideX: (y = 0, str = 500) => ({ 62 "translateX": [ 63 ['elInY', y], 64 [0, str], 65 ], 66 }), 67 slideY: (y = 0, str = 500) => ({ 68 "translateY": [ 69 ['elInY', y], 70 [0, str], 71 ], 72 }), 73 spin: (y = 1000, str = 360) => ({ 74 "rotate": [ 75 [0, y], 76 [0, str], 77 { 78 modValue: y, 79 } 80 ], 81 }), 82 flipX: (y = 1000, str = 360) => ({ 83 "rotateX": [ 84 [0, y], 85 [0, str], 86 { 87 modValue: y 88 } 89 ], 90 }), 91 flipY: (y = 1000, str = 360) => ({ 92 "rotateY": [ 93 [0, y], 94 [0, str], 95 { 96 modValue: y 97 } 98 ], 99 }), 100 jiggle: (y = 50, str = 40) => ({ 101 "skewX": [ 102 [0, y * 1, y * 2, y * 3, y * 4], 103 [0, str, 0, -str, 0], 104 { 105 modValue: y * 4, 106 } 107 ], 108 }), 109 seesaw: (y = 50, str = 40) => ({ 110 "skewY": [ 111 [0, y * 1, y * 2, y * 3, y * 4], 112 [0, str, 0, -str, 0], 113 { 114 modValue: y * 4, 115 } 116 ], 117 }), 118 zigzag: (y = 100, str = 100) => ({ 119 "translateX": [ 120 [0, y * 1, y * 2, y * 3, y * 4], 121 [0, str, 0, -str, 0], 122 { 123 modValue: y * 4, 124 } 125 ], 126 }), 127 hueRotate: (y = 600, str = 360) => ({ 128 "hue-rotate": [ 129 [0, y], 130 [0, str], 131 { 132 modValue: y, 133 } 134 ], 135 }), 136 } 137 138 const laxInstance = (() => { 139 const transformKeys = ["perspective", "scaleX", "scaleY", "scale", "skewX", "skewY", "skew", "rotateX", "rotateY", "rotate"] 140 const filterKeys = ["blur", "hue-rotate", "brightness"] 141 const translate3dKeys = ["translateX", "translateY", "translateZ"] 142 143 const pxUnits = ["perspective", "border-radius", "blur", "translateX", "translateY", "translateZ"] 144 const degUnits = ["hue-rotate", "rotate", "rotateX", "rotateY", "skew", "skewX", "skewY"] 145 146 function getArrayValues(arr, windowWidth) { 147 if (Array.isArray(arr)) return arr 148 149 const keys = Object.keys(arr).map(x => parseInt(x)).sort((a, b) => a > b ? 1 : -1) 150 151 let retKey = keys[keys.length - 1] 152 for (let i = 0; i < keys.length; i++) { 153 const key = keys[i] 154 if (windowWidth < key) { 155 retKey = key 156 break 157 } 158 } 159 160 return arr[retKey] 161 } 162 163 function lerp(start, end, t) { 164 return start * (1 - t) + end * t 165 } 166 167 function invlerp(a, b, v) { 168 return (v - a) / (b - a) 169 } 170 171 function interpolate(arrA, arrB, v, easingFn) { 172 let k = 0 173 174 arrA.forEach((a) => { 175 if (a < v) k++ 176 }) 177 178 if (k <= 0) { 179 return arrB[0] 180 } 181 182 if (k >= arrA.length) { 183 return arrB[arrA.length - 1] 184 } 185 186 const j = k - 1 187 188 let vector = invlerp(arrA[j], arrA[k], v) 189 if (easingFn) vector = easingFn(vector) 190 const lerpVal = lerp(arrB[j], arrB[k], vector) 191 return lerpVal 192 } 193 194 const easings = { 195 easeInQuad: t => t * t, 196 easeOutQuad: t => t * (2 - t), 197 easeInOutQuad: t => t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t, 198 easeInCubic: t => t * t * t, 199 easeOutCubic: t => (--t) * t * t + 1, 200 easeInOutCubic: t => t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1, 201 easeInQuart: t => t * t * t * t, 202 easeOutQuart: t => 1 - (--t) * t * t * t, 203 easeInOutQuart: t => t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t, 204 easeInQuint: t => t * t * t * t * t, 205 easeOutQuint: t => 1 + (--t) * t * t * t * t, 206 easeInOutQuint: t => t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t, 207 easeOutBounce: t => { 208 const n1 = 7.5625 209 const d1 = 2.75 210 211 if (t < 1 / d1) { 212 return n1 * t * t 213 } else if (t < 2 / d1) { 214 return n1 * (t -= 1.5 / d1) * t + 0.75 215 } else if (t < 2.5 / d1) { 216 return n1 * (t -= 2.25 / d1) * t + 0.9375 217 } else { 218 return n1 * (t -= 2.625 / d1) * t + 0.984375 219 } 220 }, 221 easeInBounce: t => { 222 return 1 - easings.easeOutBounce(1 - t) 223 }, 224 easeOutBack: t => { 225 const c1 = 1.70158 226 const c3 = c1 + 1 227 228 return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2) 229 }, 230 easeInBack: t => { 231 const c1 = 1.70158 232 const c3 = c1 + 1 233 234 return c3 * t * t * t - c1 * t * t 235 }, 236 } 237 238 function flattenStyles(styles) { 239 const flattenedStyles = { 240 transform: '', 241 filter: '' 242 } 243 244 const translate3dValues = { 245 translateX: 0.00001, 246 translateY: 0.00001, 247 translateZ: 0.00001 248 } 249
250 Object.keys(styles).forEach((key) => { 251 const val = styles[key] 252 const unit = pxUnits.includes(key) ? 'px' : (degUnits.includes(key) ? 'deg' : '') 253 254 if (translate3dKeys.includes(key)) { 255 translate3dValues[key] = val 256 } else if (transformKeys.includes(key)) { 257 flattenedStyles.transform += `${key}(${val}${unit}) ` 258 } else if (filterKeys.includes(key)) { 259 flattenedStyles.filter += `${key}(${val}${unit}) ` 260 } else { 261 flattenedStyles[key] = `${val}${unit} ` 262 } 263 }) 264 265 flattenedStyles.transform = `translate3d(${translate3dValues.translateX}px, ${translate3dValues.translateY}px, ${translate3dValues.translateZ}px) ${flattenedStyles.transform}` 266 267 return flattenedStyles 268 } 269 270 // https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes 271 function getScrollPosition() { 272 const supportPageOffset = window.pageXOffset !== undefined 273 const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat') 274 275 const x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft 276 const y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop 277 278 return [y, x] 279 } 280 281 function parseValue(val, { width, height, x, y }, index) { 282 if (typeof val === 'number') { 283 return val 284 } 285 286 const pageHeight = document.body.scrollHeight 287 const pageWidth = document.body.scrollWidth 288 const screenWidth = window.innerWidth 289 const screenHeight = window.innerHeight 290 const [scrollTop, scrollLeft] = getScrollPosition() 291 292 const left = x + scrollLeft 293 const right = left + width 294 const top = y + scrollTop 295 const bottom = top + height 296 297 return Function(`return ${val 298 .replace(/screenWidth/g, screenWidth) 299 .replace(/screenHeight/g, screenHeight) 300 .replace(/pageHeight/g, pageHeight) 301 .replace(/pageWidth/g, pageWidth) 302 .replace(/elWidth/g, width) 303 .replace(/elHeight/g, height) 304 .replace(/elInY/g, top - screenHeight) 305 .replace(/elOutY/g, bottom) 306 .replace(/elCenterY/g, top + (height / 2) - (screenHeight / 2)) 307 .replace(/elInX/g, left - screenWidth) 308 .replace(/elOutX/g, right) 309 .replace(/elCenterX/g, left + (width / 2) - (screenWidth / 2)) 310 .replace(/index/g, index) 311 }`)() 312 } 313 314 class LaxDriver { 315 getValueFn 316 name = '' 317 lastValue = 0 318 frameStep = 1 319 m1 = 0 320 321 m2 = 0 322 inertia = 0 323 inertiaEnabled = false 324 325 326 constructor(name, getValueFn, options = {}) { 327 this.name = name 328 this.getValueFn = getValueFn 329 330 Object.keys(options).forEach((key) => { 331 this[key] = options[key] 332 }) 333 334 this.lastValue = this.getValueFn(0) 335 } 336 337 getValue = (frame) => { 338 let value = this.lastValue 339 340 if (frame % this.frameStep === 0) { 341 value = this.getValueFn(frame) 342 } 343 344 if (this.inertiaEnabled) { 345 const delta = value - this.lastValue 346 const damping = 0.8 347 348 this.m1 = this.m1 * damping + delta * (1 - damping) 349 this.m2 = this.m2 * damping + this.m1 * (1 - damping) 350 this.inertia = Math.round(this.m2 * 5000) / 15000 351 } 352 353 this.lastValue = value 354 return [this.lastValue, this.inertia] 355 } 356 } 357 358 class LaxElement { 359 domElement 360 transformsData 361 styles = {} 362 selector = '' 363 364 groupIndex = 0 365 laxInstance 366 367 onUpdate 368 369 constructor(selector, laxInstance, domElement, transformsData, groupIndex = 0, options = {}) { 370 this.selector = selector 371 this.laxInstance = laxInstance 372 this.domElement = domElement 373 this.transformsData = transformsData 374 this.groupIndex = groupIndex 375 376 const { style = {}, onUpdate } = options 377 378 Object.keys(style).forEach(key => { 379 domElement.style.setProperty(key, style[key]) 380 }) 381 382 if (onUpdate) this.onUpdate = onUpdate 383 384 this.calculateTransforms() 385 } 386 387 update = (driverValues, frame) => { 388 const { transforms } = this 389 390 const styles = {} 391 392 for (let driverName in transforms) { 393 const styleBindings = transforms[driverName] 394 395 if (!driverValues[driverName]) { 396 console.error("No lax driver with name: ", driverName) 397 } 398 399 const [value, inertiaValue] = driverValues[driverName] 400 401 for (let key in styleBindings) { 402 const [arr1, arr2, options = {}] = styleBindings[key] 403 const { modValue, frameStep = 1, easing, inertia, inertiaMode, cssFn, cssUnit = '' } = options 404 405 const easingFn = easings[easing] 406 407 if (frame % frameStep === 0) { 408 const v = modValue ? value % modValue : value 409 410 let interpolatedValue = interpolate(arr1, arr2, v, easingFn) 411 412 if (inertia) { 413 let inertiaExtra = inertiaValue * inertia 414 if (inertiaMode === 'absolute') inertiaExtra = Math.abs((inertiaExtra)) 415 interpolatedValue += inertiaExtra 416 } 417 418 const unit = cssUnit || pxUnits.includes(key) ? 'px' : (degUnits.includes(key) ? 'deg' : '') 419 const dp = unit === 'px' ? 0 : 3 420 const val = interpolatedValue.toFixed(dp) 421 styles[key] = cssFn ? cssFn(val, this.domElement) : val + cssUnit 422 } 423 } 424 } 425 426 this.applyStyles(styles) 427 if (this.onUpdate) this.onUpdate(driverValues, this.domElement) 428 } 429 430 calculateTransforms = () => { 431 this.transforms = {} 432 const windowWidth = this.laxInstance.windowWidth 433 434 for (let driverName in this.transformsData) { 435 let styleBindings = this.transformsData[driverName] 436 437 const parsedStyleBindings = {} 438 439 const { presets = [] } = styleBindings 440
441 presets.forEach((presetString) => { 442 443 const [presetName, y, str] = presetString.split(":") 444 445 const presetFn = window.lax.presets[presetName] 446 447 if (!presetFn) { 448 console.error("Lax preset cannot be found with name: ", presetName) 449 } else { 450 const preset = presetFn(y, str) 451 452 Object.keys(preset).forEach((key) => { 453 styleBindings[key] = preset[key] 454 }) 455 } 456 }) 457 458 delete styleBindings.presets 459 460 for (let key in styleBindings) { 461 let [arr1 = [-1e9, 1e9], arr2 = [-1e9, 1e9], options = {}] = styleBindings[key] 462 463 const saveTransform = this.domElement.style.transform 464 this.domElement.style.removeProperty("transform") 465 const bounds = this.domElement.getBoundingClientRect() 466 this.domElement.style.transform = saveTransform 467 468 const parsedArr1 = getArrayValues(arr1, windowWidth).map(i => parseValue(i, bounds, this.groupIndex)) 469 const parsedArr2 = getArrayValues(arr2, windowWidth).map(i => parseValue(i, bounds, this.groupIndex)) 470 471 parsedStyleBindings[key] = [parsedArr1, parsedArr2, options] 472 } 473 474 this.transforms[driverName] = parsedStyleBindings 475 } 476 } 477 478 applyStyles = (styles) => { 479 const mergedStyles = flattenStyles(styles) 480 481 Object.keys(mergedStyles).forEach((key) => { 482 this.domElement.style.setProperty(key, mergedStyles[key]) 483 }) 484 } 485 } 486 487 class Lax { 488 drivers = [] 489 elements = [] 490 frame = 0 491 492 debug = false 493 494 windowWidth = 0 495 windowHeight = 0 496 presets = laxPresets 497 498 debugData = { 499 frameLengths: [] 500 } 501 502 init = () => { 503 this.findAndAddElements() 504 505 window.requestAnimationFrame(this.onAnimationFrame) 506 this.windowWidth = document.body.clientWidth 507 this.windowHeight = document.body.clientHeight 508 509 window.onresize = this.onWindowResize 510 } 511 512 onWindowResize = () => { 513 const changed = document.body.clientWidth !== this.windowWidth || 514 document.body.clientHeight !== this.windowHeight 515 516 if (changed) { 517 this.windowWidth = document.body.clientWidth 518 this.windowHeight = document.body.clientHeight 519 this.elements.forEach(el => el.calculateTransforms()) 520 } 521 } 522 523 onAnimationFrame = (e) => { 524 if (this.debug) { 525 this.debugData.frameStart = Date.now() 526 } 527 528 const driverValues = {} 529 530 this.drivers.forEach((driver) => { 531 driverValues[driver.name] = driver.getValue(this.frame) 532 }) 533 534 this.elements.forEach((element) => { 535 element.update(driverValues, this.frame) 536 }) 537 538 if (this.debug) { 539 this.debugData.frameLengths.push(Date.now() - this.debugData.frameStart) 540 } 541 542 if (this.frame % 60 === 0 && this.debug) { 543 const averageFrameTime = Math.ceil((this.debugData.frameLengths.reduce((a, b) => a + b, 0) / 60)) 544 console.log(`Average frame calculation time: ${averageFrameTime}ms`) 545 this.debugData.frameLengths = [] 546 } 547 548 this.frame++ 549 550 window.requestAnimationFrame(this.onAnimationFrame) 551 } 552 553 addDriver = (name, getValueFn, options = {}) => { 554 this.drivers.push( 555 new LaxDriver(name, getValueFn, options) 556 ) 557 } 558 559 removeDriver = (name) => { 560 this.drivers = this.drivers.filter(driver => driver.name !== name) 561 } 562 563 findAndAddElements = () => { 564 this.elements = [] 565 const elements = document.querySelectorAll(".lax") 566 567 elements.forEach((domElement) => { 568 const driverName = "scrollY" 569 const presets = [] 570 571 domElement.classList.forEach((className) => { 572 if (className.includes("lax_preset")) { 573 const preset = className.replace("lax_preset_", "") 574 presets.push(preset) 575 } 576 }) 577 578 const transforms = { 579 [driverName]: { 580 presets 581 } 582 } 583 584 this.elements.push(new LaxElement('.lax', this, domElement, transforms, 0, {})) 585 }) 586 } 587 588 addElements = (selector, transforms, options = {}) => { 589 const domElements = options.domElements || document.querySelectorAll(selector) 590
591 domElements.forEach((domElement, i) => { 592 this.elements.push(new LaxElement(selector, this, domElement, transforms, i, options)) 593 }) 594 } 595 596 removeElements = (selector) => { 597 this.elements = this.elements.filter(element => element.selector !== selector) 598 } 599 600 addElement = (domElement, transforms, options) => { 601 this.elements.push(new LaxElement('', this, domElement, transforms, 0, options)) 602 } 603 604 removeElement = (domElement) => { 605 this.elements = this.elements.filter(element => element.domElement !== domElement) 606 } 607 } 608 609 return new Lax() 610 })() 611 612 if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') 613 module.exports = laxInstance 614 else 615 window.lax = laxInstance 616})()
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.