let mm = gsap.matchMedia(); CustomEase.create( "loaderEase", "M0,0,C0,0,0.10,0.34,0.238,0.442,0.305,0.506,0.322,0.514,0.396,0.54,0.478,0.568,0.468,0.56,0.522,0.584,0.572,0.606,0.61,0.719,0.714,0.826,0.798,0.912,1,1,1,1" ); CustomEase.create("primary-ease", "M0,0 C0.15,0 0.15,1 1,1"); CustomEase.create( "slideEase", "M0,0 C0.071,0.505 0.192,0.726 0.318,0.852 0.45,0.984 0.504,1 1,1" ); // Lenis things let lenis; Konva.pixelRatio = 1; let preloaderLottie; let pageLottie; let contactLottie; const lottieWrap = document.querySelector(".preloader-lottie"); const footerLottieWrap = document.querySelector(".lottie-holder"); const contactLottieWrap = document.querySelector(".contact-lottie"); const loadUpElements = document.querySelectorAll("[load-up]"); const loadFadeElements = document.querySelectorAll("[load-fade]"); const loaderProgress = document.querySelector(".loader_progress"); const loader = document.querySelector(".preloader"); pageLottie = lottie.loadAnimation({ container: footerLottieWrap, renderer: "svg", loop: false, autoplay: false, path: "https://cdn.prod.website-files.com/6762c3ab555175366e3368ed/67f50430b68a3ba8c2366b0d_Robot_FooterWordmark_Red.json", }); if (loader && getComputedStyle(loader).display === "flex") { let animationCompleted = false; preloaderLottie = lottie.loadAnimation({ container: lottieWrap, renderer: "svg", loop: false, autoplay: false, path: "https://cdn.prod.website-files.com/674ee1defa0316684b52d0f1/67599393ca97704fb7b75d2e_Robot_PreLoader_V3_Red.json", }); contactLottie = lottie.loadAnimation({ container: contactLottieWrap, renderer: "svg", loop: false, autoplay: false, path: "https://cdn.prod.website-files.com/674ee1defa0316684b52d0f1/67599393ca97704fb7b75d2e_Robot_PreLoader_V3_Red.json", }); // Set Lottie playback speed (optional) preloaderLottie.setSpeed(2); preloaderLottie.addEventListener("DOMLoaded", () => { const lottieDuration = 2; preloaderLottie.play(); let counter = { value: 0, }; function updateLoaderText() { let progress = Math.round(counter.value); $(".preloader-progress").text(progress); } gsap.to(counter, { value: 99, onUpdate: updateLoaderText, duration: lottieDuration, ease: "loaderEase", }); preloaderLottie.addEventListener("complete", () => { animationCompleted = true; // Complete the counter to 100 and then fire loaderComplete immediately gsap.to(counter, { value: 100, onUpdate: updateLoaderText, duration: 0.2, ease: "power2.out", onComplete: loaderComplete, }); }); }); function loaderComplete() { preloaderLottie.pause(); const preloaderTl = gsap.timeline(); preloaderTl.to(loader, { clipPath: "inset(0% 0% 100% 0%)", duration: 0.8, ease: "primary-ease", }); preloaderTl.fromTo( ".home-hero_bg", { scale: 2, // clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)", }, { scale: 1, // clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", duration: 2, ease: "expo.out", }, "<" ); preloaderTl.fromTo( ".home-hero_vid", { yPercent: 20, clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)", }, { yPercent: 0, clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", duration: 1.4, ease: "expo.out", }, "<15%" ); } } else { if (loader) loader.style.display = "none"; } $("[pixel-container]").each(function () { const container = $(this); const img = container.find("img"); if (!img.length) return; const imgSrc = img.attr("src"); const width = container.width(); const height = container.height(); const initialPixelSize = parseInt(container.attr("pixel-container")) || 80; img.remove(); const konvaDiv = $("
") .css({ width, height }) .appendTo(container); Konva.Image.fromURL(imgSrc, function (konvaImage) { const stage = new Konva.Stage({ container: konvaDiv[0], width, height, }); const layer = new Konva.Layer(); konvaImage.setAttrs({ x: 0, y: 0, width, height, }); konvaImage.cache(); konvaImage.filters([Konva.Filters.Pixelate]); konvaImage.pixelSize(initialPixelSize); layer.add(konvaImage); stage.add(layer); let pixelateTween; const galleryThumb = container.closest("[gallery-thumb]")[0]; if (galleryThumb) { const observer = new MutationObserver(() => { const isActive = $(galleryThumb).hasClass("is-active"); if (pixelateTween) pixelateTween.destroy(); pixelateTween = new Konva.Tween({ node: konvaImage, duration: 0.4, pixelSize: isActive ? 1 : initialPixelSize, onUpdate: () => layer.batchDraw(), }); pixelateTween.play(); }); observer.observe(galleryThumb, { attributes: true, attributeFilter: ["class"], }); } }); }); function lenisHorizontal() { lenis = new Lenis({ orientation: "horizontal", infinite: true, // syncTouch: true, // syncTouchLerp: 0.1, }); lenis.on("scroll", ScrollTrigger.update); gsap.ticker.add((time) => { lenis.raf(time * 1000); }); gsap.ticker.lagSmoothing(0); lenis.scrollTo(0, { immediate: true, }); } function lenisVert() { lenis = new Lenis(); lenis.on("scroll", ScrollTrigger.update); gsap.ticker.add((time) => { lenis.raf(time * 1000); }); gsap.ticker.lagSmoothing(0); } // Decide which one to run based on the URL window.addEventListener("DOMContentLoaded", () => { if (window.location.href.includes("contact")) { lenisHorizontal(); contactInfinite(); } else { lenisVert(); } if (document.querySelector(".threejs-img")) { robotThree(); } }); // Lenis things function nextPrev() { const currentArticleNumber = parseInt($(".current-project").text()); const $nextProjectItems = $(".next-project_item"); // Cache the jQuery collection // Find the next item first let $nextItem = null; $nextProjectItems.each(function () { const nextArticleNumber = parseInt( $(this).find(".next-project_name").text() ); if (nextArticleNumber === currentArticleNumber + 1) { $nextItem = $(this); return false; // Break the loop once found } }); // If no next item found, look for item with number 1 (reset case) if (!$nextItem) { $nextProjectItems.each(function () { const nextArticleNumber = parseInt( $(this).find(".next-project_name").text() ); if (nextArticleNumber === 1) { $nextItem = $(this); return false; // Break the loop once found } }); } // Remove all items except the next one $nextProjectItems.each(function () { if (this !== $nextItem[0]) { // Compare DOM elements $(this).remove(); // Remove from DOM entirely } else { $(this).addClass("current-next"); // Keep and mark as current } }); } nextPrev(); function introTransition() { let transitionTrigger = $(".transition-holder"); let transitionPanels = $(".transition-el"); let transitionTl = gsap.timeline({ scrollTrigger: { trigger: transitionTrigger, start: "top bottom", end: "top top", endTrigger: ".home-content_section", scrub: true, }, defaults: { ease: "primary-ease", }, }); transitionTl.fromTo( transitionPanels, { top: "100%", }, { top: "0%", stagger: { each: 0.1, from: "random", ease: "primary-ease", }, } ); } // introTransition(); $("[img-hover_wrap]").each(function () { let hoverImgs = $(this).find(".hover-img"); let interval; $(this).hover( function () { let index = 0; $(".hover-img").removeClass("show-img"); $(hoverImgs[index]).addClass("show-img"); index++; interval = setInterval(() => { $(".hover-img").removeClass("show-img"); $(hoverImgs[index]).addClass("show-img"); index = (index + 1) % hoverImgs.length; }, 400); }, function () { clearInterval(interval); $(".hover-img").removeClass("show-img"); } ); gsap.set(".img-list", { xPercent: -50, yPercent: -50 }); let xTo = gsap.quickTo(".img-list", "x", { duration: 0.6, ease: "power3" }); let yTo = gsap.quickTo(".img-list", "y", { duration: 0.6, ease: "power3" }); window.addEventListener("mousemove", (e) => { xTo(e.clientX); yTo(e.clientY); }); }); mm.add("(min-width: 992px) and (pointer: fine)", () => { let scrambleLinks = $("[data-cursor]"); scrambleLinks.each(function () { let text = $(this).text(); $(this).on("mouseenter", function () { gsap.to($(this), { scrambleText: { text: text, chars: "$&*6RBT0", speed: 1, // tweenLength: false, }, duration: 0.4, }); }); }); }); $("[video-player]").each(function (index) { let playVidBtn = $(this).find("[video-trigger]"); let videoPopupEl = $(this).find(".video-popup"); let closeVidBtn = $(this).find("[video-close]"); let videoItem = $(this).find(".plyr_component"); let videoBg = $(this).find(".video-popup_bg"); let thisComponent = $(this); const controls = `
00:00
`; let player = new Plyr(thisComponent.find(".plyr_video")[0], { controls, resetOnEnd: true, }); let videoPopupTl = gsap.timeline({ paused: true, defaults: { ease: "primary-ease", duration: 0.5, }, onStart: () => { lenis.stop(); }, onReverseComplete: () => { lenis.start(); }, }); videoPopupTl.set(videoPopupEl, { display: "flex", }); videoPopupTl.to(videoBg, { opacity: 1, }); videoPopupTl.fromTo( videoItem, { opacity: 0, }, { opacity: 1, }, "<" ); videoPopupTl.from( closeVidBtn, { opacity: 0, duration: 0.6, }, "<" ); videoPopupTl.set(videoBg, { pointerEvents: "auto", }); playVidBtn.on("click", function () { videoPopupTl.timeScale(1).restart(); player.play(); }); closeVidBtn.on("click", function () { videoPopupTl.timeScale(2).reverse(); player.pause(); }); player.on("ended", (event) => { videoPopupTl.timeScale(2).reverse(); }); mm.add("(min-width: 992px) and (pointer: fine)", () => { player.on("pause", () => { $("[h-cursor_text]").text("Play"); }); player.on("play", () => { $("[h-cursor_text]").text("Pause"); }); }); }); // $("[data-scroll]").each(function (index) { // let floatingText = gsap.timeline({ // scrollTrigger: { // trigger: ".home-content_section", // start: "top bottom", // end: "bottom top", // // markers: true, // scrub: true, // }, // }); // mm.add("(min-width: 992px)", () => { // floatingText.to($(this), { // top: "130%", // ease: "linear", // }); // }); // }); $("[data-scroll]").each(function (index) { let floatingEl = gsap.timeline({ scrollTrigger: { trigger: "[data-scroll-trigger]", start: "clamp(top bottom)", end: "clamp(bottom top)", // markers: true, scrub: true, }, }); mm.add("(min-width: 992px)", () => { floatingEl.to($(this), { y: "-10rem", ease: "linear", }); }); }); $("[push-down-wrap]").each(function (index) { let pushEl = $(this).find("[push-down]"); let floatingEl = gsap.timeline({ scrollTrigger: { trigger: $(this), start: "clamp(top top)", end: "clamp(bottom top)", pin: pushEl, pinSpacing: false, scrub: true, }, }); // mm.add("(min-width: 992px)", () => { floatingEl.to(pushEl, { yPercent: -100, ease: "linear", }); // }); }); // $("[smooth-pin]").each(function (index) { let pinEl = $(this).find("[pin-el]"); let pinnedTl = gsap.timeline({ scrollTrigger: { trigger: $(this), start: "clamp(top bottom)", end: "clamp(bottom bottom)", // markers: true, // pinSpacing: false, scrub: true, }, }); mm.add("(min-width: 992px)", () => { pinnedTl.to(pinEl, { y: "200vh", ease: "sine.inOut", }); }); }); $("[smooth-slow]").each(function (index) { let pinElSmooth = $(this); let pinnedSmoothTl = gsap.timeline({ scrollTrigger: { trigger: $(this), start: "clamp(top top)", end: "clamp(bottom bottom)", endTrigger: "[smooth-pin]", pin: pinElSmooth, pinSpacing: false, scrub: true, }, }); mm.add("(min-width: 992px)", () => { pinnedSmoothTl.to(pinElSmooth, { yPercent: -20, ease: "none", }); }); }); //contact-panel document.addEventListener("DOMContentLoaded", () => { let footerItems = $(".footer-component"); let footerLottie = gsap.timeline({ scrollTrigger: { trigger: "[footer-trigger]", start: "bottom bottom", // markers: true, onEnter: () => { footerLottie.restart(); pageLottie.setSpeed(1.3); pageLottie.goToAndPlay(0, true); }, }, }); footerLottie.fromTo( footerItems.find("[data-shift]"), { clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)", yPercent: -100, }, { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", yPercent: 0, stagger: { each: 0.065, }, ease: "primary-ease", duration: 0.5, } ); }); function init() { function menuTransition() { let menuOpenTrigger = $("[menu-open]"); let menuCloseTrigger = $(".menu-close"); let menuBg = $(".menu-bg"); let menuEl = $(".menu-el"); let menuOverlay = $(".menu-overlay"); let menuContent = $(".menu-layout"); let menuOpenTl = gsap.timeline({ paused: true, defaults: { ease: "circ.inOut", duration: 1, }, }); menuOpenTl.set(menuEl, { display: "flex", // clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", }); menuOpenTl.fromTo( menuOverlay, { opacity: 0, }, { opacity: 0.6, ease: "sine.inOut", } ); menuOpenTl.fromTo( menuBg, { top: "-100%", }, { top: "0%", }, "<" ); menuOpenTl.fromTo( menuContent.find("[data-shift"), { yPercent: -100, clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", }, { yPercent: 0, clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", stagger: 0.025, ease: "expo.out", duration: 1, }, "<50%" ); menuOpenTl.fromTo( menuEl.find("[menu-fade"), { opacity: 0, }, { opacity: 1, ease: "expo.out", duration: 1, }, "<" ); menuOpenTrigger.on("click", function () { menuOpenTl.timeScale(1).restart(); lenis.stop(); }); menuCloseTrigger.on("click", function () { menuOpenTl.timeScale(2).reverse(); lenis.start(); }); } menuTransition(); const date = new Date(); const options = { hour: "2-digit", minute: "2-digit", }; // Manually append GMT+2 const timeString = date.toLocaleTimeString("en-GB", options) + " (GMT+2)"; $("[display-time]").text(timeString); mm.add("(min-width: 992px) and (pointer: fine)", () => { gsap.set(".cursor", { xPercent: 0, yPercent: 0 }); let xTo = gsap.quickTo(".cursor", "x", { duration: 0.6, ease: "power3" }); let yTo = gsap.quickTo(".cursor", "y", { duration: 0.6, ease: "power3" }); window.addEventListener("mousemove", (e) => { xTo(e.clientX + 20); yTo(e.clientY + 20); }); let links = document.querySelectorAll("[move-cursor]"); links.forEach((link) => { let text = link.getAttribute("move-cursor"); let target = $(this); link.addEventListener("mouseenter", () => { $("[h-cursor_text]").text(text); gsap.set(".cursor [h-cursor_text]", { scrambleText: { text: text, chars: "$&*6RBT0", speed: 0.2, }, duration: 1, }); }); link.addEventListener("mousemove", () => { $("body").addClass("c-vis"); }); link.addEventListener("mouseleave", () => { $("body").removeClass("c-vis"); }); }); }); $("[count-items=wrap]").each(function () { const countWrap = $(this); const itemCounts = {}; // Object to store counts per category // Find all countable items inside this wrap countWrap.find("[count-items]").each(function () { const countType = $(this).attr("count-items"); // Ignore "wrap" and "total" elements if (countType !== "wrap" && countType !== "total") { itemCounts[countType] = (itemCounts[countType] || 0) + 1; } }); // Update total elements based on their specific category countWrap.find("[count-items^=total]").each(function () { const totalType = $(this).attr("count-items").replace("total-", ""); const countValue = itemCounts[totalType] || 0; $(this).text(countValue.toString().padStart(2, "0")); }); }); // $("[data-pixelate]").each(function () { // const container = $(this); // const originalImage = container.find("img"); // const originalVideo = container.find("video"); // const canvas = $('')[0]; // container.append(canvas); // const ctx = canvas.getContext("2d"); // function setCanvasDimensions() { // const rect = container[0].getBoundingClientRect(); // const dpr = window.devicePixelRatio || 1; // canvas.style.width = rect.width + "px"; // canvas.style.height = rect.height + "px"; // canvas.width = rect.width * dpr; // canvas.height = rect.height * dpr; // ctx.scale(dpr, dpr); // return { width: rect.width, height: rect.height, dpr: dpr }; // } // function createPixelation(pixelSize) { // let mediaElement = originalImage.length // ? originalImage[0] // : originalVideo[0]; // if (!mediaElement) return; // if (mediaElement.tagName === "IMG" && !mediaElement.complete) { // mediaElement.onload = function () { // createPixelation(pixelSize); // }; // return; // } // const rect = container[0].getBoundingClientRect(); // const { width, height } = rect; // canvas.width = width; // canvas.height = height; // const mediaWidth = mediaElement.videoWidth || mediaElement.naturalWidth; // const mediaHeight = // mediaElement.videoHeight || mediaElement.naturalHeight; // const mediaAspectRatio = mediaWidth / mediaHeight; // const canvasAspectRatio = width / height; // let drawWidth, drawHeight, offsetX, offsetY; // if (mediaAspectRatio > canvasAspectRatio) { // drawHeight = height; // drawWidth = mediaWidth * (height / mediaHeight); // offsetX = (width - drawWidth) / 2; // offsetY = 0; // } else { // drawWidth = width; // drawHeight = mediaHeight * (width / mediaWidth); // offsetX = 0; // offsetY = (height - drawHeight) / 2; // } // const tempCanvas = document.createElement("canvas"); // const tempCtx = tempCanvas.getContext("2d"); // tempCanvas.width = Math.ceil(drawWidth / pixelSize); // tempCanvas.height = Math.ceil(drawHeight / pixelSize); // tempCtx.drawImage( // mediaElement, // 0, // 0, // tempCanvas.width, // tempCanvas.height // ); // ctx.imageSmoothingEnabled = false; // ctx.clearRect(0, 0, width, height); // ctx.drawImage( // tempCanvas, // 0, // 0, // tempCanvas.width, // tempCanvas.height, // offsetX, // offsetY, // drawWidth, // drawHeight // ); // } // createPixelation(10); // if (originalVideo.length) { // originalVideo.on("play", function () { // setInterval(() => createPixelation(10), 10); // }); // } // $(window).on("resize", function () { // createPixelation(12); // }); // }); // USE THIS TO RELOAD PAGE GEOFF // document.addEventListener("visibilitychange", function () { // if (document.hidden) { // // User has left - start the timer // awayTime = Date.now(); // } else { // // User has returned - check how long they were away // const returnTime = Date.now(); // const timeAway = (returnTime - awayTime) / 1000; // convert to seconds // if (timeAway > 50) { // alert("You were away for more than 50 seconds!"); // } // console.log(`User was away for ${timeAway.toFixed(1)} seconds.`); // } // }); $(".featured-work").each(function () { let featuredItems = $(this).find(".featured-work_item"); let projName = $(this).find(".project-slide_name"); let projIndex = $(this).find(".current-project_number"); let slideNext = $(this).find(".slide-next"); let slidePrev = $(this).find(".slide-prev"); let currentIndex = 0; let interval; let isHovered = false; gsap.set(featuredItems, { flex: "auto", }); function cycleHover() { if (!isHovered) { currentIndex = (currentIndex + 1) % featuredItems.length; triggerHover(featuredItems.eq(currentIndex)); } } function triggerHover($item) { let $this = $item; let $siblings = featuredItems.not($item); let $name = $item.find(".hidden-name").text(); let $currentNumber = $item.index() + 1; featuredItems.removeClass("is-active"); $item.addClass("is-active"); let workTl = gsap.timeline(); gsap.to($this, { width: "80%", ease: "primary-ease", duration: 0.5, onComplete: function () { // alert("test"); }, }); gsap.to($this.find("canvas"), { ease: "primary-ease", duration: 0.5, }); gsap.set($this.find("canvas"), { // width: "400%", opacity: 0, }); gsap.to($siblings, { width: `${20 / $siblings.length}%`, ease: "primary-ease", duration: 0.5, }); gsap.set($siblings.find("canvas"), { opacity: 1, }); let currentText = projName.text(); // Check if the text is already the same as $name if (currentText !== $name) { gsap.to(projName, { scrambleText: { text: $name, chars: "$&*~%^#6RBT0", speed: 1, }, duration: 0.4, }); } projIndex.text( $currentNumber < 10 ? "0" + $currentNumber : $currentNumber ); currentIndex = featuredItems.index($this); } function startAutoCycle() { interval = setInterval(cycleHover, 3000); } function stopAutoCycle() { clearInterval(interval); } // Navigation functions for next/prev buttons function goToNext(e) { if (e) e.preventDefault(); stopAutoCycle(); currentIndex = (currentIndex + 1) % featuredItems.length; triggerHover(featuredItems.eq(currentIndex)); // Only restart auto cycle on desktop if not hovering if ( window.matchMedia("(min-width: 992px) and (pointer: fine)").matches && !isHovered ) { startAutoCycle(); } } function goToPrev(e) { if (e) e.preventDefault(); stopAutoCycle(); currentIndex = (currentIndex - 1 + featuredItems.length) % featuredItems.length; triggerHover(featuredItems.eq(currentIndex)); // Only restart auto cycle on desktop if not hovering if ( window.matchMedia("(min-width: 992px) and (pointer: fine)").matches && !isHovered ) { startAutoCycle(); } } // Add click handlers for next/prev buttons slideNext.on("click", goToNext); slidePrev.on("click", goToPrev); mm.add("(min-width: 992px) and (pointer: fine)", () => { featuredItems.on("mouseenter", function () { isHovered = true; stopAutoCycle(); triggerHover($(this)); }); featuredItems.on("mouseleave", function () { isHovered = false; startAutoCycle(); }); }); mm.add("(max-width: 991px)", () => { featuredItems.on("click", function () { isHovered = true; stopAutoCycle(); triggerHover($(this)); }); }); // Initialize the first item and start auto cycle triggerHover(featuredItems.eq(0)); // featuredItems.eq(0).mouseenter(); // featuredItems.eq(0).hover(); startAutoCycle(); }); $(".categories-section").each(function () { let categoryTitles = $(this).find(".category-title_item"); let categoryTriggers = $(this).find(".categories-trigger_item"); let catIndex = $(this).find("[current-slide]"); let catEyebrow = $(this).find(".categories-eyebrow_item"); let catReveal = $(this).find("[cat-reveal]"); let catTl = gsap.timeline({ paused: true, }); catTl.fromTo( ".nav-eyebrow", { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", yPercent: 0, opacity: 1, }, { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", yPercent: 100, opacity: 0, ease: "expo.out", duration: 0.3, } ); catTl.fromTo( catReveal, { clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)", yPercent: -100, opacity: 0, }, { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", yPercent: 0, opacity: 1, stagger: { each: 0.05, }, ease: "expo.out", duration: 0.3, }, "<" ); ScrollTrigger.create({ trigger: $(this), start: "top top", end: "bottom top", endTrigger: ".categories-trigger_wrap", onEnter: () => { catTl.restart(); }, onEnterBack: () => { catTl.restart(); }, onLeaveBack: () => { catTl.reverse(); }, onLeave: () => { catTl.reverse(); }, }); categoryTriggers.each(function (index) { ScrollTrigger.create({ trigger: $(this), start: "top center", end: "bottom center", onEnter: () => { let slideNumber = String(index + 1).padStart(2, "0"); catIndex.text(slideNumber); }, onEnterBack: () => { let slideNumber = String(index + 1).padStart(2, "0"); catIndex.text(slideNumber); }, }); ScrollTrigger.create({ trigger: $(this), start: "top center", end: "bottom center", onEnter: () => { catEyebrow.removeClass("is-active"); let activeEyebrow = catEyebrow.eq(index); activeEyebrow.addClass("is-active"); }, onEnterBack: () => { catEyebrow.removeClass("is-active"); let activeEyebrow = catEyebrow.eq(index); activeEyebrow.addClass("is-active"); }, }); // Animation logic (skip first) if (index >= 1) { let transitionTl = gsap.timeline({ scrollTrigger: { trigger: $(this), start: "top bottom", end: "top top", scrub: 0.4, }, defaults: { ease: "expo.inOut", }, }); transitionTl.to(categoryTitles, { yPercent: "-=100", }); } }); }); $("[gallery-section]").each(function () { let featuredPress = $(this).find("[gallery-thumb]"); let pressName = $(this).find("[gallery-title]"); let pressIndex = $(this).find("[gallery-indicator]"); let pressWebEl = $(this).find("[press-web-target]"); let currentIndex = 0; let interval; let isHovered = false; function cycleHover() { if (!isHovered) { currentIndex = (currentIndex + 1) % featuredPress.length; triggerHover(featuredPress.eq(currentIndex)); } } function triggerHover($item) { let $this = $item; let index = featuredPress.index($this); let $siblings = featuredPress.not($item); let $name = $item.attr("press-name-trigger"); let $web = $item.attr("press-web-trigger"); let $currentNumber = index + 1; let currentText = pressName.text(); featuredPress.removeClass("is-active"); $item.addClass("is-active"); if (currentText !== $name) { gsap.to(pressName, { scrambleText: { text: $name, chars: "$&*~%^#60", speed: 1, tweenLength: false, }, duration: 0.4, }); pressWebEl.text($web); } pressIndex.text( $currentNumber < 10 ? "0" + $currentNumber : $currentNumber ); currentIndex = index; // Handle preview-wrap classes let $allWraps = $(".gallery-items_active"); let $targetWrap = $allWraps.eq(index).find("[gallery-wrap]"); $allWraps.find("[gallery-wrap]").removeClass("hovering"); $targetWrap.addClass("hovering"); } function startAutoCycle() { interval = setInterval(cycleHover, 5000); } function stopAutoCycle() { clearInterval(interval); } featuredPress.on("mouseenter", function () { isHovered = true; stopAutoCycle(); triggerHover($(this)); }); featuredPress.on("mouseleave", function () { isHovered = false; startAutoCycle(); let index = featuredPress.index($(this)); }); // Start from the first item triggerHover(featuredPress.eq(0)); startAutoCycle(); }); $(".bts-item").each(function (index) { const image = this; const imgEl = $(this).find(".bts-img"); ScrollTrigger.create({ trigger: image, start: "top bottom", end: "bottom top", scrub: true, onUpdate({ getVelocity }) { gsap.fromTo( image, { skewY: `${getVelocity() / 800}deg`, }, { skewY: 0, ease: "none", } ); gsap.fromTo( imgEl, { skewY: `-${getVelocity() / 800}deg`, }, { skewY: 0, ease: "none", }, "<" ); }, }); }); } init(); mm.add("(min-width: 992px)", () => { $("[mouse-imgs_wrap]").each(function () { const $root = $(this); const images = []; $root.find("[mouse-imgs_list] [mouse-imgs_item] img").each(function () { images.push($(this).attr("src")); }); let incr = 0; let oldIncrX = null; let oldIncrY = null; const resetDist = $(window).width() / 8; let indexImg = 0; let lastMouseX = 0; let lastMouseY = 0; let lastScrollTop = $(window).scrollTop(); // Track mouse position globally $(document).on("mousemove mouseover", function (e) { lastMouseX = e.clientX; lastMouseY = e.clientY; }); // Handle scroll events $(window).on("scroll", function () { const currentScrollTop = $(window).scrollTop(); const scrollDelta = currentScrollTop - lastScrollTop; lastScrollTop = currentScrollTop; // Check if mouse is over the element const offset = $root.offset(); const width = $root.outerWidth(); const height = $root.outerHeight(); // Convert client coordinates to page coordinates const pageX = lastMouseX + window.pageXOffset; const pageY = lastMouseY + window.pageYOffset; if ( pageX >= offset.left && pageX <= offset.left + width && pageY >= offset.top && pageY <= offset.top + height ) { // Mouse is inside the element const valX = pageX - offset.left; const valY = pageY - offset.top; if (oldIncrX === null || oldIncrY === null) { oldIncrX = valX; oldIncrY = valY; return; } // Add scroll delta to simulate mouse movement incr += Math.abs(scrollDelta); if (incr > resetDist) { incr = 0; createMedia(valX, valY, 0, -scrollDelta); } oldIncrX = valX; oldIncrY = valY; } }); $root.on("mouseenter", function () { // Reset on enter to avoid shared state incr = 0; oldIncrX = null; oldIncrY = null; }); $root.on("mousemove", function (e) { const offset = $root.offset(); const valX = e.pageX - offset.left; const valY = e.pageY - offset.top; if (oldIncrX === null || oldIncrY === null) { oldIncrX = valX; oldIncrY = valY; return; } incr += Math.abs(valX - oldIncrX) + Math.abs(valY - oldIncrY); if (incr > resetDist) { incr = 0; createMedia(valX, valY, valX - oldIncrX, valY - oldIncrY); } oldIncrX = valX; oldIncrY = valY; }); function createMedia(x, y, deltaX, deltaY) { const image = $("").attr("src", images[indexImg]).css({ position: "absolute", left: 0, top: 0, pointerEvents: "none", }); $root.append(image); const tl = gsap.timeline({ onComplete: function () { image.remove(); tl.kill(); }, }); tl.fromTo( image, { xPercent: -50 + (Math.random() - 0.5) * 80, yPercent: -50 + (Math.random() - 0.5) * 10, scaleX: 1, scaleY: 1, }, { scaleX: 1, scaleY: 1, ease: "elastic.out(2, 0.6)", duration: 0.6, } ); tl.fromTo( image, { x: x, y: y, rotation: (Math.random() - 0.5) * 20, }, { x: x + deltaX * 4, y: y + deltaY * 4, rotation: (Math.random() - 0.5) * 20, ease: "power4.out", duration: 1.5, }, "<" ); tl.to(image, { duration: 0.3, scale: 0.5, delay: 0.1, ease: "back.in(1.5)", }); indexImg = (indexImg + 1) % images.length; } }); }); // $(window).on("resize", function () { // instances.forEach(({ $container, renderer, camera, uniforms, text }) => { // const rect = $container[0].getBoundingClientRect(); // const aspectRatio = rect.width / rect.height; // camera.left = -1; // camera.right = 1; // camera.top = 1 / aspectRatio; // camera.bottom = -1 / aspectRatio; // camera.updateProjectionMatrix(); // renderer.setSize(rect.width, rect.height); // uniforms.u_texture.value = createTextTexture($container, text); // }); // }); $("[preview-trigger]").on("mouseenter", function () { const $wrap = $(this).find("[preview-wrap]"); $wrap.removeClass("closing").addClass("hovering"); }); $("[preview-trigger]").on("mouseleave", function () { const $wrap = $(this).find("[preview-wrap]"); $wrap.removeClass("hovering").addClass("closing"); }); $("[preview-wrap]").on("animationend", function () { $(this).removeClass("closing"); }); $(".sticky-intro").each(function () { let vidGrowTrigger = $(this).find(".director-intro_spacer"); let vidGrowEl = $(this).find(".sticky-director_vid"); let vidGrowWrap = $(this).find(".directors-vid_wrap"); mm.add("(min-width: 992px) and (pointer: fine)", () => { let vidGrowTl = gsap.timeline({ scrollTrigger: { trigger: vidGrowTrigger, start: "top bottom", end: "top top", scrub: true, }, defaults: { ease: "linear", }, }); vidGrowTl.to(vidGrowEl, { width: "100%", }); vidGrowTl.to( vidGrowWrap, { scale: 1, }, "<" ); }); }); $(".section-divider").each(function (index) { let dividerPanels = $(this).find(".divider-path"); gsap.set(dividerPanels, { yPercent: "random(15, 75, 15)", }); let dividerTl = gsap.timeline({ scrollTrigger: { trigger: this, start: "top bottom", end: "bottom top", scrub: true, }, }); dividerTl.to(dividerPanels, { yPercent: 0, stagger: 0.1, ease: "linear", }); }); $(".hero-title_visual").on("mousemove", function () { $(".robot-pixel").addClass("interactive"); }); $(".hero-title_visual").on("mouseleave", function () { $(".robot-pixel").removeClass("interactive"); }); $(".work-section").each(function (index) { let $clickedItem = null; gsap.registerPlugin(CustomEase); CustomEase.create( "hop", "M0,0 C0.053,0.604 0.157,0.72 0.293,0.837 0.435,0.959 0.633,1 1,1" ); const getItemWidth = () => { const temp = document.createElement("div"); temp.style.width = "var(--size--13rem)"; temp.style.position = "absolute"; document.body.appendChild(temp); const width = window.getComputedStyle(temp).width; document.body.removeChild(temp); return width; }; const formatCount = (count) => { return count < 10 ? `0${count}` : count.toString(); }; const updateCountDisplay = (count) => { $('[count-items="total-work"]').text(formatCount(count)); }; const updateCurrentPressNumber = (number) => { $(".current-press_number").text(number); }; const updateProjectName = (projectName, force = false) => { const currentText = $(".work-slide_name").text(); if (force || currentText !== projectName) { gsap.to(".work-slide_name", { scrambleText: { text: projectName, chars: "$&*~%^#6RBT0", speed: 1, }, duration: 0.4, }); } }; const updateActiveCategories = ($item) => { const $categoriesDiv = $(".active-categories_div"); $categoriesDiv.empty(); $item.find(".filter-text_hidden").each(function () { const categoryText = $(this).text(); // Only add the category if it's not "All" if (categoryText.toLowerCase() !== "all") { $("
") .addClass("u-text-style-small") .text(categoryText) .appendTo($categoriesDiv); } }); }; const activateGalleryItem = ($thumbItem, force = false) => { const originalIndex = $thumbItem.index(); const projectName = $thumbItem.attr("project-name"); const projectDirector = $thumbItem.attr("director"); const projectProducer = $thumbItem.attr("producer"); const directorTarget = $("[director-target]"); const producerTarget = $("[producer-target]"); const $targetGalleryItem = $galleryItems.eq(originalIndex); if (force || !$targetGalleryItem.hasClass("active-project")) { $galleryItems.removeClass("active-project"); $targetGalleryItem.addClass("active-project"); $galleryItems.removeClass("hovering"); $targetGalleryItem.addClass("hovering"); updateCurrentPressNumber($thumbItem.attr("data-category-number")); updateProjectName(projectName); directorTarget.text(projectDirector); producerTarget.text(projectProducer); } updateActiveCategories($thumbItem); }; const $items = $(".work-thumbs_list"); const $filterButtons = $(".filters-item"); const $galleryItems = $(".gallery-items_active"); let itemsWidth = $items[0].scrollWidth; let containerWidth = $(window).width(); let currentX = 0; let targetX = 0; const lerpFactor = 0.1; function handleHoverState() { $(".work-thumbs_item").on("mouseenter", function () { $(".work-thumbs_item").removeClass("is-active"); // remove from all $(this).addClass("is-active"); // add to hovered item activateGalleryItem($(this)); // activate the item }); $(".work-thumbs_item").on("mouseleave", function () { $(".work-thumbs_item").removeClass("is-active"); }); } $filterButtons.on("click", function () { const $this = $(this); $filterButtons.removeClass("active active-category"); $this.addClass("active active-category"); const filterText = $this .find(".filter-text_hidden") .text() .toLowerCase() .trim(); filterItems(filterText); // $items }); function filterItems(filter) { const $allItems = $(".work-thumbs_item"); const finalWidth = getItemWidth(); let visibleItemCount = 0; let $firstVisibleItem = null; const visibleItems = $allItems.filter(function () { const itemText = $(this).text().toLowerCase(); return ( filter === "all" || filter === "featured" || itemText.includes(filter) ); }); $allItems.each(function () { const $item = $(this); const itemText = $item.text().toLowerCase(); const isCurrentlyHidden = $item.is(":hidden"); if ( filter === "all" || filter === "featured" || itemText.includes(filter) ) { visibleItemCount++; $item.attr("data-category-number", formatCount(visibleItemCount)); if (!$firstVisibleItem) { $firstVisibleItem = $item; } if (isCurrentlyHidden) { gsap.set($item, { display: "flex", width: "25px" }); gsap.to($item, { width: finalWidth, ease: "hop", duration: 1, onComplete: updateItemsWidth, }); } } else { gsap.set($item, { display: "none", width: "0px" }); $item.removeAttr("data-category-number"); } }); updateCountDisplay(visibleItemCount); if ($firstVisibleItem) { activateGalleryItem($firstVisibleItem, true); $clickedItem = $firstVisibleItem; $(".work-thumbs_item").removeClass("is-active"); $clickedItem.addClass("is-active"); } resetPosition(); setTimeout(() => { mm.add("(min-width: 992px) and (pointer: fine)", () => { updateItemsWidth(); applyMouseMoveEffect(); }); }, 1000); // handleItemClick(); } function updateItemsWidth() { itemsWidth = $items[0].scrollWidth; } function resetPosition() { gsap.to($items, { x: 0, ease: "power2.out", duration: 0.5 }); currentX = 0; targetX = 0; mm.add("(max-width: 991px)", () => { $(".work-thumbs_wrap.w-dyn-list").animate({ scrollLeft: 0 }, 200); }); } function applyMouseMoveEffect() { $items.off("mousemove"); if (itemsWidth > containerWidth) { $items.on("mousemove", handleMouseMove); } } function handleMouseMove(e) { const mouseX = e.clientX; const maxScroll = itemsWidth - containerWidth; const percentage = mouseX / containerWidth; targetX = -maxScroll * percentage; } function animate() { currentX += (targetX - currentX) * lerpFactor; gsap.set($items, { x: currentX }); requestAnimationFrame(animate); } function initialize() { const $firstFilter = $filterButtons.first(); $firstFilter.addClass("active active-category"); const initialFilterText = $firstFilter .find(".filter-text_hidden") .text() .toLowerCase() .trim(); animate(); filterItems(initialFilterText); // Delay the click until after everything is set up setTimeout(() => { $(".work-thumbs_item:visible").first().trigger("click"); $(".filters-item").first().trigger("click"); }, 200); } initialize(); handleHoverState(); $(window).on("resize", function () { containerWidth = $(window).width(); updateItemsWidth(); mm.add("(min-width: 992px) and (pointer: fine)", () => { applyMouseMoveEffect(); }); }); mm.add("(min-width: 992px) and (pointer: fine)", () => { let catReveal = $(this).find("[cat-reveal]"); let catTl = gsap.timeline({ paused: true, }); catTl.fromTo( ".nav-eyebrow", { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", yPercent: 0, opacity: 1, }, { clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)", yPercent: 100, opacity: 0, ease: "expo.out", duration: 0.3, } ); catTl.fromTo( catReveal, { clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)", yPercent: -100, opacity: 0, }, { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)", yPercent: 0, opacity: 1, stagger: { each: 0.05, }, ease: "expo.out", duration: 0.3, }, "<" ); ScrollTrigger.create({ trigger: $(this), start: "top +10px", end: "bottom -10px", endTrigger: ".work-section", // markers: true, onEnter: () => { catTl.restart(); }, onEnterBack: () => { catTl.restart(); }, onLeaveBack: () => { catTl.reverse(); }, onLeave: () => { catTl.reverse(); }, }); }); }); // function galleryFilter() { function lostSkulls() { class LostSkull { constructor() { this.x = Math.random() * (lostWidth - lostSkullSize.width); this.y = Math.random() * (lostHeight - lostSkullSize.height); this.vx = (Math.random() - 0.5) * 4; // Random x velocity this.vy = (Math.random() - 0.5) * 4; // Random y velocity } update() { this.x += this.vx; this.y += this.vy; // Slight random motion over time this.vx += (Math.random() - 0.5) * 0.1; this.vy += (Math.random() - 0.5) * 0.1; // Limit maximum speed to avoid chaos let speedLimit = 4; this.vx = Math.max(-speedLimit, Math.min(this.vx, speedLimit)); this.vy = Math.max(-speedLimit, Math.min(this.vy, speedLimit)); // Wall collision if (this.x <= 0 || this.x + lostSkullSize.width >= lostWidth) { this.vx *= -1; this.x = Math.max(0, Math.min(this.x, lostWidth - lostSkullSize.width)); } if (this.y <= 0 || this.y + lostSkullSize.height >= lostHeight) { this.vy *= -1; this.y = Math.max( 0, Math.min(this.y, lostHeight - lostSkullSize.height) ); } } draw() { lostCtx.drawImage( lostSkullImage, this.x, this.y, lostSkullSize.width, lostSkullSize.height ); } } let lostCanvas; let lostCtx; let lostWidth, lostHeight; let lostSkullImage; let lostTick = 0; let lostSkulls = []; let lostSkullSize = { width: 0, height: 0 }; function lostSetup() { lostCanvas = document.querySelector(".lost-canvas"); lostCtx = lostCanvas.getContext("2d"); lostReset(); lostSkullImage = new Image(); lostSkullImage.onload = () => { lostSkullSize = { width: lostSkullImage.naturalWidth, height: lostSkullImage.naturalHeight, }; lostInitSkulls(); lostDraw(); // Start animation loop only after image is loaded }; lostSkullImage.src = "https://cdn.prod.website-files.com/6762c3ab555175366e3368ed/67ff78d855f46aa1c8926467_robot-404.svg"; window.addEventListener("resize", () => { lostReset(); }); } function lostInitSkulls() { lostSkulls = []; for (let i = 0; i < 10; i++) { lostSkulls.push(new LostSkull()); } } function lostReset() { lostWidth = lostCanvas.width = window.innerWidth; lostHeight = lostCanvas.height = window.innerHeight; } function lostDraw() { requestAnimationFrame(lostDraw); lostCtx.fillStyle = "rgba(41, 15, 240, 1)"; lostCtx.fillRect(0, 0, lostWidth, lostHeight); // Update and draw lostSkulls.forEach((skull, index) => { skull.update(); for (let j = index + 1; j < lostSkulls.length; j++) { let other = lostSkulls[j]; if ( skull.x < other.x + lostSkullSize.width && skull.x + lostSkullSize.width > other.x && skull.y < other.y + lostSkullSize.height && skull.y + lostSkullSize.height > other.y ) { // They overlap // Calculate centres let centreSkullX = skull.x + lostSkullSize.width / 2; let centreSkullY = skull.y + lostSkullSize.height / 2; let centreOtherX = other.x + lostSkullSize.width / 2; let centreOtherY = other.y + lostSkullSize.height / 2; // Differences let dx = centreSkullX - centreOtherX; let dy = centreSkullY - centreOtherY; // Calculate distance let distance = Math.sqrt(dx * dx + dy * dy) || 1; // avoid division by zero // Normalise let nx = dx / distance; let ny = dy / distance; // Calculate relative velocity let vxRel = skull.vx - other.vx; let vyRel = skull.vy - other.vy; // Dot product (how much they are moving toward each other) let dot = vxRel * nx + vyRel * ny; if (dot < 0) { // Only bounce if they are moving toward each other // Exchange momentum skull.vx -= dot * nx; skull.vy -= dot * ny; other.vx += dot * nx; other.vy += dot * ny; } // Push them apart slightly let pushAmount = 1; skull.x += nx * pushAmount; skull.y += ny * pushAmount; other.x -= nx * pushAmount; other.y -= ny * pushAmount; } } skull.draw(); }); } lostSetup(); } $(".directors-all").each(function () { // Store references to elements within this specific .directors-all section var $thisSection = $(this); var $directorsItem = $thisSection.find(".directors-item"); var $directorsWrap = $thisSection.find(".directors-wrap"); mm.add("(min-width: 992px) and (pointer: fine)", () => { if ($directorsItem.length && $directorsWrap.length) { var updateDirectorsWrapPadding = function () { var itemHeight = $directorsItem.outerHeight(); var paddingBottom = itemHeight * 3; $directorsWrap.css("padding-bottom", paddingBottom + "px"); }; updateDirectorsWrapPadding(); $(window).on("resize", updateDirectorsWrapPadding); } }); }); function robotThree() { var GUI = lil.GUI; function clamp(number, min, max) { return Math.max(min, Math.min(number, max)); } class Sketch { constructor(options) { this.scene = new THREE.Scene(); this.container = options.dom; this.img = this.container.querySelector("img"); this.width = this.container.offsetWidth; this.height = this.container.offsetHeight; this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); this.renderer.setSize(this.width, this.height); this.renderer.setClearColor(0xeeeeee, 0); this.renderer.physicallyCorrectLights = true; this.renderer.outputEncoding = THREE.sRGBEncoding; this.container.appendChild(this.renderer.domElement); this.camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 100 ); var frustumSize = 1; var aspect = this.width / this.height; this.camera = new THREE.OrthographicCamera( frustumSize / -2, frustumSize / 2, frustumSize / 2, frustumSize / -2, -1000, 1000 ); this.camera.position.set(0, 0, 2); this.time = 0; this.mouse = { x: 0, y: 0, prevX: 0, prevY: 0, vX: 0, vY: 0, }; this.isPlaying = true; this.settings(); this.addObjects(); this.resize(); this.render(); this.setupResize(); this.mouseEvents(); } getValue(val) { return parseFloat(this.container.getAttribute("data-" + val)); } mouseEvents() { // Mouse event handler this.container.addEventListener("mousemove", (e) => { this.handlePointerMove(e.clientX, e.clientY); }); // Touch event handlers this.container.addEventListener("touchstart", (e) => { if (e.touches.length > 0) { // Prevent scrolling when interacting with the element e.preventDefault(); const touch = e.touches[0]; this.handlePointerMove(touch.clientX, touch.clientY); } }); this.container.addEventListener("touchmove", (e) => { if (e.touches.length > 0) { // Prevent scrolling when interacting with the element e.preventDefault(); const touch = e.touches[0]; this.handlePointerMove(touch.clientX, touch.clientY); } }); } // Unified handler for both mouse and touch events handlePointerMove(clientX, clientY) { const rect = this.container.getBoundingClientRect(); this.mouse.x = (clientX - rect.left) / rect.width; this.mouse.y = (clientY - rect.top) / rect.height; this.mouse.vX = this.mouse.x - this.mouse.prevX; this.mouse.vY = this.mouse.y - this.mouse.prevY; this.mouse.prevX = this.mouse.x; this.mouse.prevY = this.mouse.y; } settings() { let that = this; this.settings = { grid: this.getValue("grid") || 34, mouse: this.getValue("mouse") || 0.25, strength: this.getValue("strength") || 1, relaxation: this.getValue("relaxation") || 0.9, }; this.gui = new GUI(); this.gui.add(this.settings, "grid", 2, 1000, 1).onFinishChange(() => { this.regenerateGrid(); }); this.gui.add(this.settings, "mouse", 0, 1, 0.01); this.gui.add(this.settings, "strength", 0, 1, 0.01); this.gui.add(this.settings, "relaxation", 0, 1, 0.01); } setupResize() { mm.add("(min-width: 992px) and (pointer: fine)", () => { window.addEventListener("resize", this.resize.bind(this)); }); } resize() { this.width = this.container.offsetWidth; this.height = this.container.offsetHeight; this.renderer.setSize(this.width, this.height); this.camera.aspect = this.width / this.height; // image cover this.imageAspect = 1.5085 / 1; let a1; let a2; if (this.height / this.width > this.imageAspect) { a1 = (this.width / this.height) * this.imageAspect; a2 = 1; } else { a1 = 1; a2 = this.height / this.width / this.imageAspect; } this.material.uniforms.resolution.value.x = this.width; this.material.uniforms.resolution.value.y = this.height; this.material.uniforms.resolution.value.z = a1; this.material.uniforms.resolution.value.w = a2; this.camera.updateProjectionMatrix(); this.regenerateGrid(); } regenerateGrid() { this.size = this.settings.grid; const width = this.size; const height = this.size; const size = width * height; const data = new Float32Array(3 * size); const color = new THREE.Color(0xffffff); const r = Math.floor(color.r * 255); const g = Math.floor(color.g * 255); const b = Math.floor(color.b * 255); for (let i = 0; i < size; i++) { let r = Math.random() * 255 - 125; let r1 = Math.random() * 255 - 125; const stride = i * 3; data[stride] = r; data[stride + 1] = r1; data[stride + 2] = r; } // used the buffer to create a DataTexture this.texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat, THREE.FloatType ); this.texture.magFilter = this.texture.minFilter = THREE.NearestFilter; if (this.material) { this.material.uniforms.uDataTexture.value = this.texture; this.material.uniforms.uDataTexture.value.needsUpdate = true; } } addObjects() { this.regenerateGrid(); let texture = new THREE.TextureLoader().load(this.img.src); texture.needsUpdate = true; this.material = new THREE.ShaderMaterial({ extensions: { derivatives: "#extension GL_OES_standard_derivatives : enable", }, side: THREE.DoubleSide, uniforms: { time: { value: 0, }, resolution: { value: new THREE.Vector4(), }, uTexture: { value: texture, }, uDataTexture: { value: this.texture, }, }, vertexShader: `uniform float time; varying vec2 vUv; varying vec3 vPosition; uniform vec2 pixels; float PI = 3.141592653589793238; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, fragmentShader: `uniform float time; uniform float progress; uniform sampler2D uDataTexture; uniform sampler2D uTexture; uniform vec4 resolution; varying vec2 vUv; varying vec3 vPosition; float PI = 3.141592653589793238; void main() { vec2 newUV = (vUv - vec2(0.5))*resolution.zw + vec2(0.5); vec4 color = texture2D(uTexture,newUV); vec4 offset = texture2D(uDataTexture,vUv); gl_FragColor = vec4(vUv,0.0,1.); gl_FragColor = vec4(offset.r,0.,0.,1.); gl_FragColor = color; gl_FragColor = texture2D(uTexture,newUV - 0.02*offset.rg); // gl_FragColor = offset; }`, }); this.geometry = new THREE.PlaneGeometry(1, 1, 1, 1); this.plane = new THREE.Mesh(this.geometry, this.material); this.scene.add(this.plane); } updateDataTexture() { let data = this.texture.image.data; for (let i = 0; i < data.length; i += 3) { data[i] *= this.settings.relaxation; data[i + 1] *= this.settings.relaxation; } let gridMouseX = this.size * this.mouse.x; let gridMouseY = this.size * (1 - this.mouse.y); let maxDist = this.size * this.settings.mouse; let aspect = this.height / this.width; for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { let distance = (gridMouseX - i) ** 2 / aspect + (gridMouseY - j) ** 2; let maxDistSq = maxDist ** 2; if (distance < maxDistSq) { let index = 3 * (i + this.size * j); let power = maxDist / Math.sqrt(distance); power = clamp(power, 0, 10); // if(distance { // Image appearance gsap.fromTo( el.querySelector("img"), { transformOrigin: "0% 50%", // Starting from the left edge }, { transformOrigin: "0% 50%", scaleX: 1, // Returning to initial width ease: "none", // Linear movement scrollTrigger: { horizontal: true, trigger: el, // Listen to the image position start: "left 100%", // Starts when the left edge of the image is on the right side of the viewport end: "left 80%", // Ends when the left edge of the image is at 80% of the viewport scrub: true, // Progresses with the scroll }, } ); // Disappearance of the image gsap.fromTo( el.querySelector("img"), { transformOrigin: "100% 50%", }, { transformOrigin: "100% 50%", scaleX: 0, // Squashing the image ease: "none", // Linear movement immediateRender: false, // To avoid tween conflicts scrollTrigger: { horizontal: true, trigger: el, // Listen to the image position start: "right 20%", // Starts when the right edge of the image is at 20% of the viewport end: "right 0%", // Ends when the right edge of the image is at 0% of the viewport scrub: true, // Progresses with the scroll }, } ); }); } $("[data-email-address]").on("click", function () { const $el = $(this); const email = $el.attr("data-email-address"); const original = $el.text(); function showCopied() { $el.text("[ COPIED TO CLIPBOARD ]"); setTimeout(() => $el.text(original), 1000); } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(email).then(showCopied).catch(showCopied); } else { // Fallback for old browsers const temp = $("").val(email).appendTo("body").select(); try { document.execCommand("copy"); } catch (err) { console.error("Fallback copy failed", err); } temp.remove(); showCopied(); } });