window.__STYLEGALLERY_ASSETS__={}; console.clear(); gsap.registerPlugin(Observer); const scrollingText = gsap.utils.toArray(".rail h4"); const tl = horizontalLoop(scrollingText, { repeat: -1, paddingRight: 30 }); Observer.create({ onChangeY(self) { let factor = 2.5; if (self.deltaY < 0) { factor *= -1; } gsap.timeline({ defaults: { ease: "none" } }).to(tl, { timeScale: factor * 2.5, duration: 0.2, overwrite: true }).to(tl, { timeScale: factor / 2.5, duration: 1 }, "+=0.3"); } }); function horizontalLoop(items, config) { items = gsap.utils.toArray(items); config = config || {}; let tl2 = gsap.timeline({ repeat: config.repeat, paused: config.paused, defaults: { ease: "none" }, onReverseComplete: () => tl2.totalTime(tl2.rawTime() + tl2.duration() * 100) }), length = items.length, startX = items[0].offsetLeft, times = [], widths = [], xPercents = [], curIndex = 0, pixelsPerSecond = (config.speed || 1) * 100, snap = config.snap === false ? (v) => v : gsap.utils.snap(config.snap || 1), totalWidth, curX, distanceToStart, distanceToLoop, item, i; gsap.set(items, { // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. xPercent: (i2, el) => { let w = widths[i2] = parseFloat(gsap.getProperty(el, "width", "px")); xPercents[i2] = snap(parseFloat(gsap.getProperty(el, "x", "px")) / w * 100 + gsap.getProperty(el, "xPercent")); return xPercents[i2]; } }); gsap.set(items, { x: 0 }); totalWidth = items[length - 1].offsetLeft + xPercents[length - 1] / 100 * widths[length - 1] - startX + items[length - 1].offsetWidth * gsap.getProperty(items[length - 1], "scaleX") + (parseFloat(config.paddingRight) || 0); for (i = 0; i < length; i++) { item = items[i]; curX = xPercents[i] / 100 * widths[i]; distanceToStart = item.offsetLeft + curX - startX; distanceToLoop = distanceToStart + widths[i] * gsap.getProperty(item, "scaleX"); tl2.to(item, { xPercent: snap((curX - distanceToLoop) / widths[i] * 100), duration: distanceToLoop / pixelsPerSecond }, 0).fromTo(item, { xPercent: snap((curX - distanceToLoop + totalWidth) / widths[i] * 100) }, { xPercent: xPercents[i], duration: (curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond, immediateRender: false }, distanceToLoop / pixelsPerSecond).add("label" + i, distanceToStart / pixelsPerSecond); times[i] = distanceToStart / pixelsPerSecond; } function toIndex(index, vars) { vars = vars || {}; Math.abs(index - curIndex) > length / 2 && (index += index > curIndex ? -length : length); let newIndex = gsap.utils.wrap(0, length, index), time = times[newIndex]; if (time > tl2.time() !== index > curIndex) { vars.modifiers = { time: gsap.utils.wrap(0, tl2.duration()) }; time += tl2.duration() * (index > curIndex ? 1 : -1); } curIndex = newIndex; vars.overwrite = true; return tl2.tweenTo(time, vars); } tl2.next = (vars) => toIndex(curIndex + 1, vars); tl2.previous = (vars) => toIndex(curIndex - 1, vars); tl2.current = () => curIndex; tl2.toIndex = (index, vars) => toIndex(index, vars); tl2.times = times; tl2.progress(1, true).progress(0, true); if (config.reversed) { tl2.vars.onReverseComplete(); tl2.reverse(); } return tl2; }