Jump to content
Search Community

Max Hart

Premium
  • Posts

    6
  • Joined

  • Last visited

About Max Hart

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Max Hart's Achievements

  1. I'm importing CustomEase from the dist folder to get around SSR / to prevent this error: Cannot use import statement outside a module However, despite registering the plugin after importing, i'm still given a warning message in my terminal that i need to register it. Register is working with the same approach for many other plugins, seems to just be specific to this one. Here's my utility file where i'm handling all things GSAP: // src/lib/gsap/index.tsx import gsap from "gsap"; import { CustomEase } from "gsap/dist/CustomEase"; import { SplitText } from "gsap/dist/SplitText"; import { ScrollTrigger } from "gsap/dist/ScrollTrigger"; import { ScrambleTextPlugin } from "gsap/dist/ScrambleTextPlugin"; import { TextPlugin } from "gsap/dist/TextPlugin"; import { GSDevTools } from "gsap/dist/GSDevTools"; gsap.registerPlugin( SplitText, ScrollTrigger, ScrambleTextPlugin, CustomEase, GSDevTools, TextPlugin, ); const GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2; const RECIPROCAL_GR = 1 / GOLDEN_RATIO; const DURATION = RECIPROCAL_GR; const MICROSTAGGER = 0.05; const STAGGER = 0.5; const FADEDISTANCE = 100; const EASE = CustomEase.create("ease", "0.175, 0.885, 0.32, 1"); // Configuring GSAP with custom settings that aren't Tween-specific gsap.config({ autoSleep: 60, nullTargetWarn: false, }); // Setting default animation properties that should be inherited by ALL tweens gsap.defaults({ duration: DURATION, ease: EASE, }); // Once the desired configurations are set, we simply export what we need to work with in the future. export { DURATION, EASE, STAGGER, MICROSTAGGER, FADEDISTANCE, GOLDEN_RATIO, gsap, SplitText, ScrollTrigger, GSDevTools, };
  2. I think i've solved this by doing the following: Separate timeline for .loadin animation / elements Separate timeline for .loadout animation / elements loadin animation on timeline using .from .loadout animation on seperate timeline using .to const tl_scroll = useRef(gsap.timeline({ defaults: { opacity: 0, duration: 1 } })); const tl_loadin = useRef(gsap.timeline({ defaults: { opacity: 0, duration: 1 } })); const q = gsap.utils.selector(ref); useEffect(() => { const ctx = gsap.context(() => { gsap.set([q('.loadin'), q('.loadout')], { autoAlpha: 1 }); tl_loadin.current.from(q('.loadin'), { opacity: 0, y: 200, stagger: 0.125, duration: 1.5, ease: 'elastic.out(1, 0.5)', }); tl_scroll.current = gsap .timeline({ scrollTrigger: { trigger: ref.current, animation: tl_scroll.current, start: 'top top', end: 'bottom bottom', immediateRender: false, markers: true, scrub: true, onToggle: (self) => { if (self.isActive) { console.log('Im active!'); } }, }, }) .to('.loadout', { opacity: 0, y: -200, stagger: 0.125 }); }, ref); return () => ctx.revert(); Annoyingly, the HTML elements being used for each timeline need to be different, incase the user starts scrolling through the tl_scroll timeline before the tl_loadin timeline has finished animating (it'll try animating opacity from 1 to 0, but opacity will still be changing from 0 to 1 from the tl_loadin animation) - a small price to pay for the desired outcome! Here's how that would look: <div className="loadout"> <h1 className="loadin">{formattedTitle}</h1> </div> <div className="loadin"> <div className="loadout"> {content} </div> </div> <div className="loadin"> <a className="loadout" href={buttonUrl}> {buttonText} </a> </div>
  3. Thanks for that @mvaneijgen - i believe i've incorrectly explained my problem, let me try to clarify and please let me know if it makes sense. Imagine this was a hero element at the top of a page (100vh); i want the content within it to have an 'on load' (e.g. fadeup) when the user lands on the page - unless the user has started to scroll down the page, and the hero is on its way out of the viewport - if that user journey has begun, it should load the timeline position of the contents exit animation (e.g. fadeleft) based on the scroll distance of that journey. Animation B should be scrolltrigger controlled and should only start playing when the user begins scrolling to the next 100vh section of the page. Would be good to know how you'd approach something like that and if i'm going down the total wrong route?
  4. Hoping someone can point me in the right direction. What i'm trying to achieve here is: Fade in animation from [y:200, opacity:0] to [y:0, opacity: 1] - ideally when the page loads / when the section first comes into the viewport When the user scrolls down another 100vh - the elements are transitioned from [y:0, opacity: 1] to [y:200, opacity: 0] and vice versa based on the scroll direction Am i going wrong somewhere currently? In the grander scheme of things this approach will be used for an 'apple-esque' page that fades new content in / out based on the users scroll position - hopefully this logic will be easy to apply on a larger scale once i understand the concept of how to achieve this Any help appreciated!
  5. Don't suppose anyone knows of a working example with Nextjs 13 App directory? Having a similar issue where i create my ScrollSmoother in /app/layout.tsx, but when navigating to /page-1/, parallax doesn't work and also doesn't when navigating back to the root page / Here's my /app/layout.tsx file: "use client"; import { Manrope, Rock_Salt } from "@next/font/google"; import { PropsWithChildren, useRef, useState } from "react"; import Providers from "../redux/providers/providers"; import { gsap, ScrollSmoother } from "../utils/gsap"; import Navigation from "../ui/Navigation"; import { SmootherContext } from "../utils/SmootherContext"; import { useCorrectEffect } from "../utils/useCorrectEffect"; import Cta from "../ui/Cta"; import Footer from "../ui/Footer"; import PageShrinker from "../ui/PageShrinker"; import "styles/globals.css"; const manrope = Manrope({ variable: "--display-font", weight: ["200", "300", "400", "500", "600", "700", "800"], }); const rock_salt = Rock_Salt({ variable: "--body-font", weight: ["400"], }); const siteTitle = ""; type Props = PropsWithChildren; export default function RootLayout({ children }: Props) { const app = useRef<HTMLDivElement>(null); const [smoother, setSmoother] = useState<ScrollSmoother | undefined>(); useCorrectEffect(() => { let mm = gsap.matchMedia(app); mm.add("not (pointer:coarse)", () => { const smoother = ScrollSmoother.create({ smooth: 1.5, effects: true, }); setSmoother(smoother); }); }, []); return ( <html lang="en" className={`bg-black ${manrope.variable} ${rock_salt.variable}`} > <head> <title>{siteTitle}</title> <meta name="description" content={siteTitle} /> </head> <body> <Providers> <Navigation /> <SmootherContext.Provider value={smoother}> <div id="smooth-wrapper" ref={app}> <div id="smooth-content"> <PageShrinker>{children}</PageShrinker> <Footer /> </div> </div> </SmootherContext.Provider> </Providers> <noscript> <style> {` .invisible { visibility: visible !important; } `} </style> </noscript> </body> </html> ); } Am i doing this in the wrong file?
×
×
  • Create New...