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")
})
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>