React-Spring odd behaviour on first init, the animation of the first item has an weird delay?

23 views Asked by At

Trying to solve my animation problem. I have this animation where on load, i am animating buttons, and if the animation of 4s is over, it should go to the next slide, and show a different image. On the first page load, the animation of the button is done but the image and next button is animating after 4s + some weird extra 2s? after, this weird issue it works perfectly fine every 4s the animation repeats in a loop.

Any help would be great.

"use client"

import React, { useEffect, useRef, useState } from "react"

import { animated, useSprings } from "react-spring"

import { FullscreenHero as HeroType } from "@/types/page-sections/fullscreen-hero"

import { BlockContentHero } from "@/components/shared"

type HeroProps = {
  data: HeroType
}

const Hero = ({ data }: HeroProps) => {
  if (!data) return null

  const { content } = data

  const [activeButton, setActiveButton] = useState(0)
  const [hoveredButton, setHoveredButton] = useState<number | null>(null)
  const [initialRender, setInitialRender] = useState(true)
  const [imagesLoaded, setImagesLoaded] = useState(false) // Track if images are loaded

  const imageUrls = [
    "https://cdn.sanity.io/images/i4puecl6/production-v2/6d4512da62110d52ff00501a928b6224f3d4ebee-2005x1276.webp?w=1980&q=90&fit=clip&auto=format",
    "https://cdn.sanity.io/images/i4puecl6/production-v2/04010ba92f6a3c5c731795dc76a8e380edbb966b-2005x1276.webp?w=1980&q=90&fit=clip&auto=format",
    "https://cdn.sanity.io/images/i4puecl6/production-v2/ccf2101864730c320b84b4098c5345331f7b5487-3200x2400.webp?w=1980&q=90&fit=clip&auto=format"
  ]

  const easeOutQuad = (t: number) => t * (2 - t)
  const easeInOutCubic = (t: number) =>
    t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1

  useEffect(() => {
    // Preload images
    const preloadImages = async () => {
      await Promise.all(
        imageUrls.map(
          (url) =>
            new Promise((resolve) => {
              const img = new Image()
              img.src = url
              img.onload = resolve
            })
        )
      )
      setImagesLoaded(true)
    }

    preloadImages()
  }, [imageUrls])

  // Start interval immediately when component mounts
  useEffect(() => {
    startInterval()

    return () => {
      if (intervalRef.current !== null) {
        clearInterval(intervalRef.current)
      }
    }
  }, [])

  useEffect(() => {
    // Set initial state immediately
    setInitialRender(false)
  }, [])

  const imageSprings = useSprings(
    imageUrls.length,
    imageUrls.map((imageUrl, index) => ({
      opacity: activeButton === index ? 1 : 0,
      scale: activeButton === index ? 1.1 : 1,
      config: {
        duration: 200,
        easing: activeButton === index ? easeInOutCubic : easeOutQuad
      }
    }))
  )

  const contentBg = [
    { text: "Zorgwijzer", link: "/work/design-declares", imageIndex: 0 },
    { text: "Creditcard", link: "/work/roadrunner-venture-studios", imageIndex: 1 },
    { text: "WorldCup", link: "/work/only-one", imageIndex: 2 }
  ]

  const intervalRef = useRef<NodeJS.Timeout | null>(null)

  const handleMouseEnter = (index: number) => {
    setHoveredButton(index)
    setActiveButton(index)
    if (intervalRef.current !== null) {
      clearInterval(intervalRef.current)
    }
  }

  const handleMouseLeave = () => {
    setHoveredButton(null)
    startInterval() // Start a new interval when the mouse leaves
  }

  const startInterval = () => {
    intervalRef.current = setInterval(() => {
      setActiveButton((prev) => (prev + 1) % 3)
    }, 4000)
  }

  return (
    <section className="col-span-full text-white overflow-hidden absolute top-0 left-0 z-0 w-full h-full px-5 md:px-10 flex flex-col items-center justify-between bg-black">
      <div className="h-full flex items-center place-content-center">
        <h1 className="hero pt-[90px] text-center max-w-wrap z-10 mb-10">
          {contentBg.map((item: any, index: number) => (
            <span key={index} className="mx-2 sm:inline-flex">
              <a
                className={`hero overflow-hidden group text-inherit`}
                href={item.link}
                onMouseEnter={() => handleMouseEnter(index)}
                onMouseLeave={handleMouseLeave}
              >
                <span
                  className={`mix-blend-difference z-[100] uppercase relative group-hover:mix-blend-normal`}
                >
                  {item.text}
                </span>
                <span
                  className={`h-full absolute z-10 top-0 left-0 transition-all duration-500 bg-white  ${
                    activeButton === index ? "animate-progress" : "opacity-0 w-0"
                  }`}
                ></span>
              </a>
            </span>
          ))}
        </h1>
        {/* {contentBg.map((item: any, index: number) => (
          <animated.img
            key={index}
            src={imageUrls[index]}
            alt={`Background Image ${index + 1}`}
            className="absolute w-screen h-screen top-0 left-0 object-cover transition duration-500 opacity-0"
            style={{
              ...imageSprings[index],
              backgroundImage: `url(${imageUrls[index]})`
            }}
          />
        ))} */}
      </div>

      <div className="absolute right-10 w-0.5 bottom-10 h-[100px] z-40">
        <span className="animate-draw absolute w-full h-0 bg-white rounded-full"></span>
      </div>
    </section>
  )
}

export default Hero
0

There are 0 answers