1// 2// lax v1.2.3 (Alex Fox) 3// Simple & light weight vanilla javascript plugin to create beautiful animations things when you scrolllll!! 4// 5// Licensed under the terms of the MIT license. 6// 7// You may use it in your theme if you credit me. 8// It is also free to use on any individual website. 9// 10// Exception: 11// The only restriction is to not publish any 12// extension for browsers or native application 13// without getting a written permission first. 14// 15 16(() => { 17 const lax = (() => { 18 const lax = { 19 elements: [] 20 } 21 22 let lastY = 0; 23 let currentBreakpoint = 'default' 24 const breakpointsSeparator = "_" 25 26 const transformFns = { 27 "data-lax-opacity": (style, v) => { style.opacity = v }, 28 "data-lax-translate": (style, v) => { style.transform += ` translate(${v}px, ${v}px)` }, 29 "data-lax-translate-x": (style, v) => { style.transform += ` translateX(${v}px)` }, 30 "data-lax-translate-y": (style, v) => { style.transform += ` translateY(${v}px)` }, 31 "data-lax-scale": (style, v) => { style.transform += ` scale(${v})` }, 32 "data-lax-scale-x": (style, v) => { style.transform += ` scaleX(${v})` }, 33 "data-lax-scale-y": (style, v) => { style.transform += ` scaleY(${v})` }, 34 "data-lax-skew": (style, v) => { style.transform += ` skew(${v}deg, ${v}deg)` }, 35 "data-lax-skew-x": (style, v) => { style.transform += ` skewX(${v}deg)` }, 36 "data-lax-skew-y": (style, v) => { style.transform += ` skewY(${v}deg)` }, 37 "data-lax-rotate": (style, v) => { style.transform += ` rotate(${v}deg)` }, 38 "data-lax-rotate-x": (style, v) => { style.transform += ` rotateX(${v}deg)` }, 39 "data-lax-rotate-y": (style, v) => { style.transform += ` rotateY(${v}deg)` }, 40 "data-lax-brightness": (style, v) => { style.filter += ` brightness(${v}%)` }, 41 "data-lax-contrast": (style, v) => { style.filter += ` contrast(${v}%)` }, 42 "data-lax-hue-rotate": (style, v) => { style.filter += ` hue-rotate(${v}deg)` }, 43 "data-lax-blur": (style, v) => { style.filter += ` blur(${v}px)` }, 44 "data-lax-invert": (style, v) => { style.filter += ` invert(${v}%)` }, 45 "data-lax-saturate": (style, v) => { style.filter += ` saturate(${v}%)` }, 46 "data-lax-grayscale": (style, v) => { style.filter += ` grayscale(${v}%)` }, 47 "data-lax-bg-pos": (style, v) => { style.backgroundPosition = `${v}px ${v}px` }, 48 "data-lax-bg-pos-x": (style, v) => { style.backgroundPositionX = `${v}px` }, 49 "data-lax-bg-pos-y": (style, v) => { style.backgroundPositionY = `${v}px` } 50 } 51 52 let crazy = "" 53 54 for(let i=0;i<20;i++) { 55 crazy += " " + i*5 + " " + (i*47)%360 + ", " 56 } 57 58 lax.presets = { 59 linger: (v) => { 60 return { "data-lax-translate-y": `(vh*0.7) 0, 0 200, -500 0` } 61 }, 62 lazy: (v=100) => { 63 return { "data-lax-translate-y": `(vh) 0, (-elh) ${v}` } 64 }, 65 eager: (v=100) => { 66 return { "data-lax-translate-y": `(vh) 0, (-elh) -${v}` } 67 }, 68 slalom: (v=50) => { 69 return { "data-lax-translate-x": `vh ${v}, (vh*0.8) ${-v}, (vh*0.6) ${v}, (vh*0.4) ${-v}, (vh*0.2) ${v}, (vh*0) ${-v}, (-elh) ${v}` } 70 }, 71 crazy: (v) => { 72 return { "data-lax-hue-rotate": `${crazy} | loop=20` } 73 }, 74 spin: (v=360) => { 75 return { "data-lax-rotate": `(vh) 0, (-elh) ${v}` } 76 }, 77 spinRev: (v=360) => { 78 return { "data-lax-rotate": `(vh) 0, (-elh) ${-v}` } 79 }, 80 spinIn: (v=360) => { 81 return { "data-lax-rotate": `vh ${v}, (vh*0.5) 0` } 82 }, 83 spinOut: (v=360) => { 84 return { "data-lax-rotate": `(vh*0.5) 0, -elh ${v}` } 85 }, 86 blurInOut: (v=40) => { 87 return { "data-lax-blur": `(vh) ${v}, (vh*0.8) 0, (vh*0.2) 0, 0 ${v}` } 88 }, 89 blurIn: (v=40) => { 90 return { "data-lax-blur": `(vh) ${v}, (vh*0.7) 0` } 91 }, 92 blurOut: (v=40) => { 93 return { "data-lax-blur": `(vh*0.3) 0, 0 ${v}` } 94 }, 95 fadeInOut: () => { 96 return { "data-lax-opacity": "(vh) 0, (vh*0.8) 1, (vh*0.2) 1, 0 0" } 97 }, 98 fadeIn: () => { 99 return { "data-lax-opacity": "(vh) 0, (vh*0.7) 1" } 100 }, 101 fadeOut: () => { 102 return { "data-lax-opacity": "(vh*0.3) 1, 0 0" } 103 }, 104 driftLeft: (v=100) => { 105 return { "data-lax-translate-x": `vh ${v}, -elh ${-v}` } 106 }, 107 driftRight: (v=100) => { 108 return { "data-lax-translate-x": `vh ${-v}, -elh ${v}` } 109 }, 110 leftToRight: (v=1) => { 111 return { "data-lax-translate-x": `vh 0, -elh (vw*${v})` } 112 }, 113 rightToLeft: (v=1) => { 114 return { "data-lax-translate-x": `vh 0, -elh (vw*${-v})` } 115 }, 116 zoomInOut: (v=0.2) => { 117 return { "data-lax-scale": `(vh) ${v}, (vh*0.8) 1, (vh*0.2) 1, -elh ${v}` } 118 }, 119 zoomIn: (v=0.2) => { 120 return { "data-lax-scale": `(vh) ${v}, (vh*0.7) 1` } 121 }, 122 zoomOut: (v=0.2) => { 123 return { "data-lax-scale": `(vh*0.3) 1, -elh ${v}` } 124 }, 125 speedy: (v=30) => { 126 return { "data-lax-skew-x": `(vh) ${v}, -elh ${-v}` } 127 }, 128 swing: (v=30) => { 129 return { "data-lax-skew-y": `(vh) ${v}, -elh ${-v}` } 130 } 131 } 132 133 lax.addPreset = (p, o) => { 134 lax.presets[p] = o 135 } 136 137 function intrp(t, v) { 138 let i = 0 139 140 while(t[i][0] <= v && t[i+1] !== undefined) { 141 i+=1 142 } 143 144 const x = t[i][0] 145 const prevX = t[i-1] === undefined ? x : t[i-1][0] 146 147 const y = t[i][1] 148 const prevY = t[i-1] === undefined ? y : t[i-1][1] 149 150 const xPoint = Math.min(Math.max((v-prevX)/(x-prevX),0),1) 151 const yPoint = (xPoint*(y-prevY)) + prevY 152 153 return yPoint 154 } 155 156 function fnOrVal(s) { 157 if(s[0] === "(") return eval(s) 158 else return parseFloat(s) 159 } 160 161 lax.setup = (o={}) => { 162 lax.breakpoints = o.breakpoints || {} 163 164 lax.selector = o.selector || '.lax' 165 lax.populateElements() 166 } 167 168 lax.removeElement = (el) => { 169 const i = lax.elements.findIndex(o => o.el = el) 170 if(i > -1) { 171 lax.elements.splice(i, 1) 172 } 173 } 174 175 lax.createLaxObject = (el) => { 176 const o = { 177 el: el, 178 originalStyle: { 179 transform: el.style.transform, 180 filter: el.style.filter 181 }, 182 transforms: {} 183 } 184 185 return o 186 } 187 188 lax.calcTransforms = (o) => { 189 const { el } = o 190 191 //find presets in data attributes 192 const presets = [] 193 for(let i=0; i<el.attributes.length; i++) { 194 const a = el.attributes[i] 195 if(a.name.indexOf("data-lax-preset") > -1) { 196 presets.push(a); 197 } 198 } 199 200 //unwrap presets into transformations 201 for(let i=0; i<presets.length; i++) { 202 const a = presets[i] 203 const b = a.name.split(breakpointsSeparator) 204 const breakpoint = b[1] ? `${breakpointsSeparator}${b[1]}` : '' 205 a.value.split(" ").forEach((p) => { 206 const bits = p.split("-") 207 const fn = lax.presets[bits[0]] 208 if(!fn) { 209 console.log(`lax error: preset ${bits[0]} is not defined`) 210 } else { 211 const d = fn(bits[1]) 212 for(let k in d) { 213 el.setAttribute(`${k}${breakpoint}`, d[k]) 214 } 215 } 216 }) 217 218 el.setAttribute("data-lax-anchor", "self") 219 el.attributes.removeNamedItem(a.name) 220 221 } 222 223 // use gpu 224 const useGpu = !(el.attributes["data-lax-use-gpu"] && el.attributes["data-lax-use-gpu"].value === 'false') 225 if(useGpu) { 226 el.style["backface-visibility"] = "hidden" 227 el.style["-webkit-backface-visibility"] = "hidden" 228 } 229 if(el.attributes["data-lax-use-gpu"]) el.attributes.removeNamedItem("data-lax-use-gpu") 230 231 // optmise off screen 232 o.optimise = false 233 if(el.attributes["data-lax-optimize"] && el.attributes["data-lax-optimize"].value === 'true') { 234 o.optimise = true 235 const bounds = el.getBoundingClientRect() 236 el.setAttribute("data-lax-opacity", `${-bounds.height-1} 0, ${-bounds.height} 1, ${window.innerHeight} 1, ${window.innerHeight+1} 0`) 237 el.attributes.removeNamedItem("data-lax-optimize") 238 } 239 240 // build transform list 241 for(let i=0; i<el.attributes.length; i++) { 242 const a = el.attributes[i] 243 if(a.name.indexOf("data-lax") < 0) continue 244 245 const b = a.name.split(breakpointsSeparator) 246 const bits = b[0].split("-") 247 const breakpoint = b[1] || "default" 248 if(bits[1] === "lax") { 249 if(a.name === "data-lax-anchor") { 250 o["data-lax-anchor"] = a.value === "self" ? el : document.querySelector(a.value) 251 const rect = o["data-lax-anchor"].getBoundingClientRect()
252 o.anchorTop = Math.floor(rect.top) + window.scrollY 253 } else { 254 const tString = a.value 255 .replace(/vw/g, window.innerWidth) 256 .replace(/vh/g, window.innerHeight) 257 .replace(/elh/g, el.clientHeight) 258 .replace(/elw/g, el.clientWidth) 259 .replace(/\s+/g," ") 260 261 const [arrString, optionString] = tString.split("|") 262 const options = {} 263 264 if(optionString) { 265 optionString.split(" ").forEach((o) => { 266 const [key, val] = o.split("=") 267 options[key] = key && fnOrVal(val) 268 }) 269 } 270 271 const name = b[0] 272 const valueMap = arrString.split(",").map((x) => { 273 return x.trim().split(" ").map(fnOrVal) 274 }).sort((a,b) => { 275 return a[0] - b[0] 276 }) 277 278 if(!o.transforms[name]) { 279 o.transforms[name] = {} 280 } 281 282 o.transforms[name][breakpoint] = { valueMap, options } 283 } 284 } 285 } 286 287 // sprite sheet animation 288 const spriteData = el.attributes["data-lax-sprite-data"] && el.attributes["data-lax-sprite-data"].value 289 if(spriteData) { 290 o.spriteData = spriteData.split(",").map(x => parseInt(x)) 291 el.style.height = o.spriteData[1] + "px" 292 el.style.width = o.spriteData[0] + "px" 293 294 const spriteImage = el.attributes["data-lax-sprite-image"] && el.attributes["data-lax-sprite-image"].value 295 if(spriteImage) { 296 el.style.backgroundImage = `url(${spriteImage})` 297 } 298 } 299 300 return o 301 } 302 303 lax.addElement = (el) => { 304 const o = lax.calcTransforms(lax.createLaxObject(el)) 305 lax.elements.push(o) 306 lax.updateElement(o) 307 } 308 309 lax.populateElements = () => { 310 lax.elements = [] 311 document.querySelectorAll(lax.selector).forEach(lax.addElement) 312 currentBreakpoint = lax.getCurrentBreakPoint() 313 } 314 315 lax.updateElements = () => { 316 lax.elements.forEach((o) => { 317 lax.calcTransforms(o) 318 lax.updateElement(o) 319 }) 320 321 currentBreakpoint = lax.getCurrentBreakPoint() 322 } 323 324 325 lax.getCurrentBreakPoint = () => { 326 let b = 'default' 327 const w = window.innerWidth 328 329 for(let i in lax.breakpoints) { 330 const px = parseFloat(lax.breakpoints[i]) 331 if(px <= w) { 332 b = i 333 } else { 334 break 335 } 336 } 337 338 return b 339 } 340 341 lax.updateElement = (o) => { 342 const { originalStyle, anchorTop, transforms, spriteData, el } = o 343 344 let r = anchorTop ? anchorTop-lastY : lastY 345 346 const style = { 347 transform: originalStyle.transform, 348 filter: originalStyle.filter 349 } 350 351 for(let i in transforms) { 352 const transformData = transforms[i][currentBreakpoint] || transforms[i]["default"] 353 354 if(!transformData) { 355 // console.log(`lax error: there is no setting for key ${i} and screen size ${currentBreakpoint}. Try adding a default value!`) 356 continue 357 } 358 359 let _r = r 360 if(transformData.options.offset) _r = _r+transformData.options.offset 361 if(transformData.options.speed) _r = _r*transformData.options.speed 362 if(transformData.options.loop) _r = _r%transformData.options.loop 363 364 const t = transformFns[i] 365 const v = intrp(transformData.valueMap, _r) 366 367 if(!t) { 368 // console.info(`lax: error ${i} is not supported`) 369 continue 370 } 371 372 t(style, v) 373 } 374 375 if(spriteData) { 376 const [frameW,frameH,numFrames,cols,scrollStep] = spriteData 377 const frameNo = Math.floor(lastY/scrollStep) % numFrames 378 const framePosX = frameNo%cols 379 const framePosY = Math.floor(frameNo/cols) 380 style.backgroundPosition = `-${framePosX*frameW}px -${framePosY*frameH}px` 381 } 382 383 if(style.opacity === 0) { // if opacity 0 don't update 384 el.style.opacity = 0 385 } else { 386 for(let k in style) { 387 el.style[k] = style[k] 388 } 389 } 390 } 391 392 lax.update = (y) => { 393 if(lastY === y) return 394 lastY = y
395 lax.elements.forEach(lax.updateElement) 396 } 397 398 return lax; 399 })(); 400 401 if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') 402 module.exports = lax; 403 else 404 window.lax = lax; 405 })(); 406
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.