Search code examples
reactjsgsap

Sections from an array of objects


I am trying to recreate this codepen with react and gsap on here and i have been trying to recreate this for about an hour now and do not even know where to start from, please who can help. i do not want to create the sections the same way in that codepen but rather form the details from an array of objects meaning creating just once component, i am fairly new to react and i would like know how to do something like that in react, looking at it from vanillajs perspective, i already know what is going on and i also created it like this in react but i want a situation where i have minimal code and the an array of objects forms one component making it more dynamic so i can click on it to get more information about each section that comes in. kindly assist Codepen link below https://codepen.io/designsbyharp/pen/jObpWZg

HTML

<div class="slider">
  
  <div class="slider__slide slider__slide--1">
    <div class="slider__img slider__img--1"></div>
    <div class="slider__text slider__text--1">
      <h1 class="slider__header">Rejuvenate your, true self.</h1>
      <a href="ign.com" class="cta">discover</a>
    </div>
  </div>
  
  <div class="slider__slide slider__slide--2">
    <div class="slider__img slider__img--2"></div>
    <div class="slider__text slider__text--2">
      <h1 class="slider__header">Professonial, trust-worthy, and compassionate.</h1>
      <a href="google.com" class="cta">learn more</a>
    </div>
  </div>
  
    <div class="slider__slide slider__slide--3">
    <div class="slider__img slider__img--3"></div>
    <div class="slider__text slider__text--3">
      <h1 class="slider__header">Trust in us.</h1>
      <a href="youtube.com" class="cta">learn more</a>
    </div>
  </div>
  
  <div class="slider__slide slider__slide--4">
    <div class="slider__img slider__img--4"></div>
    <div class="slider__text slider__text--4">
      <h1 class="slider__header">What we do.</h1>
      <a href="tsn.ca" class="cta">discover</a>
    </div>
  </div>
  
  <div class="slider__navigation">
    <div class="slider__count slider__count--top">
      <p class="count count--top count--top-1">01</p>
      <p class="count count--top count--top-2">02</p>
      <p class="count count--top count--top-3">03</p>      
      <p class="count count--top count--top-4">04</p>
    </div>
    
    <div class="slider__bar">
      <div id="sliderBarDynamic" class="slider__bar--dynamic"></div>
      <div class="slider__bar--static"></div>
    </div>
    
     <div class="slider__count slider__count--bottom">
      <p class="count count--bottom count--bottom-1">02</p>
      <p class="count count--bottom count--bottom-2">03</p>
      <p class="count count--bottom count--bottom-3">04</p>      
      <p class="count count--bottom count--bottom-3">01</p>
    </div>
  </div>
  
</div>

CSS

@import url('https://fonts.googleapis.com/css2?family=Gilda+Display&family=Roboto&display=swap');

*,
*::before,
*::after{
padding:0;
margin:0;
box-sizing:inherit;
}

html{
font-size:16px;
box-sizing:border-box;
}

body{
font-family: 'Roboto', sans-serif;
color:#444444;
font-weight: 300;
line-height: 1.6;
}

img{
max-width:100%;
}

h1{
  font-size: 100px;
  color: #fff;
  font-family: 'Gilda Display', serif;
  font-weight: 300;
  line-height: 1;
}

h2{

}


h3{

}


P{
  color: #fff;
}

a{
text-decoration:none;
color:#ffffff;
  font-size: 24px;
}

ul{
list-style-type:none;
}



// Slider 
.slider{
  width: 100%;
  height: 100vh;
  overflow: hidden;
  position: relative;
  
  &__slide{
    width: 100%;
    height: 100%;
    display: flex;
    
    position: absolute;
    top: 0;
    left:0;
    
    &--1{
      z-index: 4;
    }
    
    &--2{
      z-index: 3;
    }
    
    &--3{
      z-index: 2;
    }
    
    &--4{
      z-index: 1;
    }
  }
  
  &__img{
    width: 100%;
    height: 100%;
    position: absolute;
    z-index: -1;
    background-size: cover;
    background-repeat: no-repeat;
    background-position: 50% 50%;
    
    &--1{
      background-image: url('https://i.postimg.cc/Y0T3F1tc/about-landing.jpg');
    }
    
    &--2{
      background-image: url('https://i.postimg.cc/FHHyKWyf/i-Stock-1148043788.jpg');
    }
    
    &--3{
      background-image: url('https://i.postimg.cc/tTqp06QH/i-Stock-1064136816.jpg');
    }
    
    &--4{
      background-image: url('https://i.postimg.cc/435R13K2/i-Stock-1179976698.jpg');
    }
  }
  
  &__text{
    align-self: flex-end;
    padding: 0 0 5vw 15vh;
    opacity: 0;
    width: 80%;
    max-width: 1005px;
    
    .slider__header{
      margin-bottom: 40px;
      text-transform: capitalize;
    }
    
    .cta{
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 6px;
      margin-left: 65px;
      position: relative;
      
      &:before{
        content: '';
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        left: -55px;
        width: 40px;
        height: 1px;
        background-color: white;
      }
    }
  }
  
  // Slider Navigation
  &__navigation{
    width: 21px;
    height: 400px;
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    left: calc(100% - 5vw);
    z-index: 10;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
  }
  
  .count--top{
    
      position: absolute;
      top: 0;
      left: 0;
    
    // position:
  }
  
  .count{
    opacity: 0;
  }
  
  .count:first-child{
    opacity: 1;
  }
  
  .count--bottom{
    position: absolute;
    bottom: 0;
    left: 0;
  }
  
  &__bar{
    width: 2px;
    height: 250px;
    position: relative;
    
    &--dynamic{
      width: 100%;
      height: 100%;
      background-color: #FF69B4;
      transform-origin: top center;
      
      position: absolute;
      top: 0;
      left: 0;
      z-index: 2;
    }
    
    &--static{
      width: 100%;
      height: 100%;
      background-color: darkgrey;
      
      position: absolute;
      top: 0;
      left: 0;
    }
  }
}
// Slider end

/*
*/

// timeline to control animations everytime the timeline restarts/repeats
let tlRepeat = gsap.timeline();

// Need first slides elements - img and text to animate on each repeat of timeline
let repeatBeginning = ()=>{
  gsap.set(bgImage[0], {opacity: 0, scale: 1.2, webkitFilter:"blur(" + 6 + "px)"})  

 tlRepeat
  .add("slide1-in")
  .fromTo([countTop[0], countBottom[0]], {opacity: 0}, {duration: 0.3, opacity: 1, ease: "Power2.easeIn"}, "slide1-in")  
  .to(bgImage[0], {duration: 1.8, scale: 1, opacity: 1, webkitFilter:"blur(" + 0 + "px)"}, "slide1-in")
  .fromTo(text[0], {opacity: 0, x: -30, ease: "Power2.easeIn"}, {duration: 0.8, opacity: 1, x: 0}, "-=1")


}

// On start animations
// let onStartSlide1Animations = ()=>{
//     // gsap.to(text[0], {duration: 0.7, opacity: 1, x: -15, ease: "Power2.easeIn"})
// }

// Variables
let slides = document.querySelectorAll('.slider__slide'),
    dynamicBar = document.querySelector('#sliderBarDynamic'),
    countTop = document.querySelectorAll(".count--top"),
    countBottom = document.querySelectorAll(".count--bottom"),
    bgImage = document.querySelectorAll(".slider__img"),
    text = document.querySelectorAll(".slider__text"),
    tl = gsap.timeline({repeat: 0, delay: 1, paused: false, onRepeat: repeatBeginning});
    
// Push all text back and only make first one visible
gsap.set(text, {x: -30});
gsap.set(text[0], {opacity: 1});


// Animate slide's elements but not the first one. 
// Make first slide's elements animate when timeline is repeating, 
// Follow the flow of rest of the slide's animations
slides.forEach((slide, i) =>{
  
  tl
    .fromTo(dynamicBar, {scaleY: 0}, {duration: 1.4, scaleY: 1}, "+=2")
    .set(dynamicBar, {transformOrigin: "bottom center"})
    .to(dynamicBar, {duration: 1, scaleY: 0}, "+=0.4")
    .set(dynamicBar, {transformOrigin: "top center"})
    .add("elements-in-out")
    .to([countTop[i], countBottom[i]], {opacity: 0}, "elements-in-out")  
    .to([countTop[i+1], countBottom[i+1]], {opacity: 1}, "elements-in-out")
    .to(bgImage[i], {duration: 0.2, opacity: 0}, "elements-in-out")
    .set(bgImage[i+1], {scale: 1.2, webkitFilter:"blur(" + 6 + "px)"}, "elements-in-out")
    .to(bgImage[i+1], {duration: 1.8, scale: 1, webkitFilter:"blur(" + 0 + "px)"}, "elements-in-out")
    .to(text[i], {duration: 0.3, opacity: 0}, "elements-in-out")
    .to(text[i+1], {duration: 0.8, opacity: 1, x: 0}, "-=1")
})










Solution

  • If I'm understanding your question correctly you want a more DRY implementation so the slides are not explicitly written out in HTML/JSX.

    Gather up the values that are different between each slide, i.e. the heading and image sources, into an array that can be mapped in the return.

    Example:

    const slides = [
      {
        id: 0,
        heading: "SCROLL",
        image: {
          src:
            "https://images.unsplash.com/photo-1567016376408-0226e4d0c1ea?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMyMDUzOA&ixlib=rb-1.2.1&q=80&w=400",
          alt: ""
        },
        overlay: {
          src:
            "https://images.unsplash.com/photo-1519710164239-da123dc03ef4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMxOTU4Mw&ixlib=rb-1.2.1&q=80&w=800",
          alt: ""
        }
      },
      {
        id: 1,
        heading: "SWIPE",
        image: {
          src:
            "https://images.unsplash.com/photo-1558603668-6570496b66f8?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMyMDUzOA&ixlib=rb-1.2.1&q=85&w=400",
          alt: ""
        },
        overlay: {
          src:
            "https://images.unsplash.com/photo-1594666757003-3ee20de41568?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMxOTcwOA&ixlib=rb-1.2.1&q=80&w=800",
          alt: ""
        }
      },
      {
        id: 2,
        heading: "SCROLL",
        image: {
          src:
            "https://images.unsplash.com/photo-1537165924986-cc3568f5d454?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMyMDU4NA&ixlib=rb-1.2.1&q=85&w=400",
          alt: ""
        },
        overlay: {
          src:
            "https://images.unsplash.com/photo-1579830341096-05f2f31b8259?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMxOTQ5Ng&ixlib=rb-1.2.1&q=80&w=800",
          alt: ""
        }
      },
      {
        id: 3,
        heading: "SWIPE",
        image: {
          src:
            "https://images.unsplash.com/photo-1589271243958-d61e12b61b97?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMyMDU4NA&ixlib=rb-1.2.1&q=80&w=400",
          alt: ""
        },
        overlay: {
          src:
            "https://images.unsplash.com/photo-1603771628302-c32c88e568e3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTY0NjMxOTUxNg&ixlib=rb-1.2.1&q=80&w=800",
          alt: ""
        }
      }
    ];
    

    Replace the repetitive JSX for mapping this slides array.

    {slides.map((slide) => (
      <section key={slide.id} class="slide">
        <div class="slide__outer">
          <div class="slide__inner">
            <div class="slide__content">
              <div class="slide__container">
                <h2 class="slide__heading">{slide.heading}</h2>
                <figure class="slide__img-cont">
                  <img
                    class="slide__img"
                    alt={slide.image.alt}
                    src={slide.image.src}
                  />
                </figure>
              </div>
            </div>
          </div>
        </div>
      </section>
    ))}
    
    <section class="overlay">
      <div class="overlay__content">
        <p class="overlay__count">
          0<span className="count">1</span>
        </p>
        <figure class="overlay__img-cont">
          {slides.map((slide) => (
            <img
              key={slide.id}
              class="image"
              src={slide.overlay.src}
              alt={slide.overlay.alt}
            />
          ))}
        </figure>
      </div>
    </section>
    

    Edit sections-from-an-array-of-objects