Instruction
Guide for Flops Webflow Template
Guide for Flops Webflow Template
<script src="https://unpkg.com/lenis@1.3.1/dist/lenis.min.js"></script>
<script>
// lenis smooth scroll
{
let lenis;
const initScroll = () => {
lenis = new Lenis({
smoothWheel: true,
smoothTouch: false,
});
// Sync GSAP with Lenis
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);
};
function initGsapGlobal() {
initScroll();
const sendGsapEvent = () => {
window.dispatchEvent(
new CustomEvent("GSAPReady", {
detail: { lenis },
})
);
};
if (document.fonts.status === "loaded") {
sendGsapEvent();
} else {
document.fonts.ready.then(() => sendGsapEvent());
}
// --- Handle resize ---
let resizeTimeout;
const onResize = () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
ScrollTrigger.refresh();
lenis.resize(); // ๐ also update Lenis' internal height
}, 150);
};
window.addEventListener("resize", onResize);
const resizeObserver = new ResizeObserver(() => onResize());
resizeObserver.observe(document.body);
// --- Handle lazy loading ---
const refreshAll = () => {
lenis.resize(); // ๐ force Lenis to recalc scroll height
ScrollTrigger.refresh(true); // ๐ force GSAP to recalc positions
};
// When all page assets (including lazy) are done
window.addEventListener("load", () => {
setTimeout(refreshAll, 200); // wait a tick to be safe
});
// For each image that loads in later
document.querySelectorAll("img").forEach((img) => {
if (!img.complete) {
img.addEventListener("load", refreshAll);
img.addEventListener("error", refreshAll); // in case of failed loads
}
});
// Reveal hidden elements after init
queueMicrotask(() => {
gsap.to("[data-start='hidden']", {
autoAlpha: 1,
duration: 0.1,
delay: 0.2,
});
});
}
const documentReady =
document.readyState === "complete" || document.readyState === "interactive";
if (documentReady) {
initGsapGlobal();
} else {
addEventListener("DOMContentLoaded", initGsapGlobal);
}
}
</script>
// HERO SECTION
// Hero image cursor fade
const images = document.querySelectorAll(".box-image");
// Start fully visible
gsap.set(images, { opacity: 1 });
let movingActive = false; // true while we're in a "moving" session
let idleTimer = null; // checks when movement stops
let dimTimer = null; // 0.3s delay before dim
let brightenTimer = null; // 0.3s delay before brighten
let dimTween = null; // reference to dim tween
let brightenTween = null; // reference to brighten tween
const IDLE_GAP = 200; // how long without movement = "stopped"
const DELAY = 300; // 0.3s before any animation starts
function startDimAfterDelay() {
// Only schedule once at the start of a moving session
if (dimTimer) return;
dimTimer = setTimeout(() => {
dimTimer = null;
// Kill any brighten in progress (movement wins)
if (brightenTween) { brightenTween.kill(); brightenTween = null; }
// Dim all to 3%
dimTween?.kill();
dimTween = gsap.to(images, { opacity: 0.03, duration: 0.2, onComplete: () => { dimTween = null; } });
}, DELAY);
}
function startBrightenAfterDelay() {
// Donโt stack multiple brightens
if (brightenTimer) return;
brightenTimer = setTimeout(() => {
brightenTimer = null;
// Ensure dim tween isn't fighting
dimTween?.kill(); dimTween = null;
// Sequential brighten back to 1
brightenTween?.kill();
brightenTween = gsap.to(images, {
opacity: 1,
duration: 1,
stagger: 0.3,
onComplete: () => { brightenTween = null; }
});
}, DELAY);
}
document.body.addEventListener("mousemove", () => {
// Movement detected: cancel "about-to-brighten"
if (brightenTimer) { clearTimeout(brightenTimer); brightenTimer = null; }
if (brightenTween) { brightenTween.kill(); brightenTween = null; }
// If we weren't moving before, this is the start of a moving session
if (!movingActive) {
movingActive = true;
startDimAfterDelay(); // schedule one dim after 0.3s
}
// Reset idle detection each move
if (idleTimer) clearTimeout(idleTimer);
idleTimer = setTimeout(() => {
// Movement stopped
movingActive = false;
// Clear any pending dim that hasn't started yet
if (dimTimer) { clearTimeout(dimTimer); dimTimer = null; }
// Brighten sequence after 0.3s
startBrightenAfterDelay();
}, IDLE_GAP);
});
// END Hero image fade cursor
// SERVICE SECTION
// Service slider
gsap.registerPlugin(Draggable);
const gallery = document.querySelector(".service-gallery-slide");
Draggable.create(gallery, {
type: "x",
bounds: ".service-gallery", // container element
inertia: true
});
// END Service slider