(Footer) Marquee animations
This script enables customizable marquee animations on elements marked with specific attributes. It allows horizontal or vertical scrolling, reverse direction, and scroll-based interaction (like changing speed or direction based on scroll velocity). The animation can also be paused via hover or click interactions. Configuration is done using marquee-* custom attributes directly in the HTML.
Attributes and their functions:
marquee-element="component"
Identifies the main container that controls the marquee animation.
marquee-element="panel"
The moving content inside the marquee. This is the element that scrolls.
marquee-element="triggerhover"
Optional element that pauses the marquee animation on hover (only works on devices with a pointer, like a mouse).
marquee-element="triggerclick"
Optional element that toggles play/pause on click. It adds or removes the is-paused class.
marquee-speed
A number that controls the animation speed. Larger values = slower scroll. Calculated as panel width or height divided by this number.
marquee-vertical
If set to true, the marquee scrolls vertically instead of horizontally.
marquee-reverse
If set to true, reverses the scroll direction (e.g., scrolls right-to-left becomes left-to-right).
marquee-scrolldirection
If true, changes the scroll direction based on the user’s page scroll direction (down = forward, up = backward).
marquee-scrollscrub
If true, the speed of the marquee responds to the velocity of the page scroll — faster scroll = faster marquee.
<script>
// This Embed is for: Marquee animations on this page.
// MARQUEE POWER-UP
window.addEventListener("DOMContentLoaded", (event) => {
// attribute value checker
function attr(defaultVal, attrVal) {
const defaultValType = typeof defaultVal;
if (typeof attrVal !== "string" || attrVal.trim() === "") return defaultVal;
if (attrVal === "true" && defaultValType === "boolean") return true;
if (attrVal === "false" && defaultValType === "boolean") return false;
if (isNaN(attrVal) && defaultValType === "string") return attrVal;
if (!isNaN(attrVal) && defaultValType === "number") return +attrVal;
return defaultVal;
}
// marquee component
$("[marquee-element='component']").each(function (index) {
let componentEl = $(this),
panelEl = componentEl.find("[marquee-element='panel']"),
triggerHoverEl = componentEl.find("[marquee-element='triggerhover']"),
triggerClickEl = componentEl.find("[marquee-element='triggerclick']");
let speedSetting = attr(100, componentEl.attr("marquee-speed")),
verticalSetting = attr(false, componentEl.attr("marquee-vertical")),
reverseSetting = attr(false, componentEl.attr("marquee-reverse")),
scrollDirectionSetting = attr(false, componentEl.attr("marquee-scrolldirection")),
scrollScrubSetting = attr(false, componentEl.attr("marquee-scrollscrub")),
moveDistanceSetting = -100,
timeScaleSetting = 1,
pausedStateSetting = false;
if (reverseSetting) moveDistanceSetting = 100;
let marqueeTimeline = gsap.timeline({ repeat: -1, onReverseComplete: () => marqueeTimeline.progress(1) });
if (verticalSetting) {
speedSetting = panelEl.first().height() / speedSetting;
marqueeTimeline.fromTo(panelEl, { yPercent: 0 }, { yPercent: moveDistanceSetting, ease: "none", duration: speedSetting });
} else {
speedSetting = panelEl.first().width() / speedSetting;
marqueeTimeline.fromTo(panelEl, { xPercent: 0 }, { xPercent: moveDistanceSetting, ease: "none", duration: speedSetting });
}
let scrubObject = { value: 1 };
ScrollTrigger.create({
trigger: "body",
start: "top top",
end: "bottom bottom",
onUpdate: (self) => {
if (!pausedStateSetting) {
if (scrollDirectionSetting && timeScaleSetting !== self.direction) {
timeScaleSetting = self.direction;
marqueeTimeline.timeScale(self.direction);
}
if (scrollScrubSetting) {
let v = self.getVelocity() * 0.006;
v = gsap.utils.clamp(-60, 60, v);
let scrubTimeline = gsap.timeline({ onUpdate: () => marqueeTimeline.timeScale(scrubObject.value) });
scrubTimeline.fromTo(scrubObject, { value: v }, { value: timeScaleSetting, duration: 0.5 });
}
}
}
});
function pauseMarquee(isPausing) {
pausedStateSetting = isPausing;
let pauseObject = { value: 1 };
let pauseTimeline = gsap.timeline({ onUpdate: () => marqueeTimeline.timeScale(pauseObject.value) });
if (isPausing) {
pauseTimeline.fromTo(pauseObject, { value: timeScaleSetting }, { value: 0, duration: 0.5 });
triggerClickEl.addClass("is-paused");
} else {
pauseTimeline.fromTo(pauseObject, { value: 0 }, { value: timeScaleSetting, duration: 0.5 });
triggerClickEl.removeClass("is-paused");
}
}
if (window.matchMedia("(pointer: fine)").matches) {
triggerHoverEl.on("mouseenter", () => pauseMarquee(true));
triggerHoverEl.on("mouseleave", () => pauseMarquee(false));
}
triggerClickEl.on("click", function () {
!$(this).hasClass("is-paused") ? pauseMarquee(true) : pauseMarquee(false);
});
});
});
</script>
(Site settings) Footer code - Text animations
This script reveals text words (and characters) with a smooth upward animation as they scroll into view. It uses GSAP, ScrollTrigger, and SplitText plugins. The animation is applied to any element with data-word-reveal="true". You can customize the animation speed and delay using HTML attributes.
Attributes and their functions:
data-word-reveal="true"
Required. Applies the scroll-based text reveal animation to the element’s child text.
data-word-duration
Optional. A number that controls the duration (speed) of the animation per word. Default is 0.8 seconds.
data-word-delay
Optional. A number that adds a delay before the animation starts. Default is 0 seconds.
<!-- GSAP Text animation -->
<script>
document.addEventListener("DOMContentLoaded", () => {
if (typeof window.gsap === "undefined") document.documentElement.classList.add("gsap-not-found");
gsap.registerPlugin(ScrollTrigger, SplitText);
});
</script>
<!-- GSAP Text animation -->
<script>
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("[data-word-reveal='true']").forEach((text) => {
const split = SplitText.create(text.children, {
type: "words, chars",
mask: "words",
wordsClass: "word",
charsClass: "char",
});
// Read custom attributes or use default values
const duration = parseFloat(text.getAttribute("data-word-duration")) || 0.8;
const delay = parseFloat(text.getAttribute("data-word-delay")) || 0;
const tl = gsap.timeline({
scrollTrigger: {
trigger: text,
start: "top bottom",
end: "top 90%",
toggleActions: "none play none reset",
},
});
tl.from(split.words, {
yPercent: 110,
delay: delay,
duration: duration,
stagger: { amount: 0.1 },
});
gsap.set(text, { visibility: "visible" });
});
});
</script>
(Site settings) Footer code - Lenis Smooth Scroll
This script adds smooth scrolling to your website using Lenis. It also ensures compatibility with GSAP ScrollTrigger, so scroll-based animations work properly. GSAP drives Lenis via its internal ticker for full animation synchronization.
<!-- Lenis Smooth Scroll -->
<script src="https://unpkg.com/lenis@1.3.1/dist/lenis.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
// Register necessary plugins (required for validation)
gsap.registerPlugin(ScrollTrigger);
// Initialize Lenis
const lenis = new Lenis({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), // expo.out
smooth: true,
smoothTouch: false,
touchMultiplier: 2
});
// Sync ScrollTrigger with Lenis
lenis.on("scroll", ScrollTrigger.update);
// Use GSAP's ticker to drive Lenis — this is required to pass GSAP validation
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
// Optional: Fire custom event to trigger delayed animations
window.dispatchEvent(new CustomEvent("GSAPReady", {
detail: { lenis }
}));
});
</script>