I am trying to recreate this codepen with react and gsap on here and i have been trying to recreate this for some hours now and do not even know where to start from. 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 making it DRY. 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.
So first off, in order to re-imagine any html snippet in terms of ReactJS, try and look for smilarities / repetation with in the snippet. Once you do that you would kind of get the gist of what can be seperated out as individual components and how it all ties together.
Now, just by looking at the html snippet, we can see that the slides are repeating, thus they can be kept as a seperate component. We also see that each slide displays a unique heading and paragraph text and also has a color scheme. So in order to make our Slide component dynamic, we can pass these as props to the component.
So our data that creates the whole Slider component (composed of Slide's) looks something like this:
const sliderData = [
{
id: '1',
headerText: `I'm the first Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#500033',
sliderIllustration: '#FF0077',
sliderInner: 'rgba(255, 0, 119, 0.4)',
sliderButton: '#FF0077',
},
},
...
]
Next we notice, some of the UI was created using CSS, and this mainly caused the whole snippet to be static. So in order to achieve dynamicity we rendered some styles with in the component(s) based on the data we are getting. Like this:
Moreover, each slide was assigned a static class like box1, box2... etc. Therefore these styles need to be rendered for each slide like this:
(Notice that the object data in the above snippet corresponds to the object in our array sliderData that shapes up the whole slider component).
Once we have our UI ready, introducing GSAP animations was simple. First we created refs to the UI elements that were being used by GSAP in the animation process.
Next up, we triggered the animations just like how you would in plain javascript. The only difference here is that we executed them in the useEffect hook that runs once the functional component is loaded (empty dependancy array).
Moreover there were a few hard-coded stuff that animated just the five slides. Which we made dynamic by introducing let ratio = 100 / sliderData.length
The whole component:
import React, { useEffect, useRef } from 'react'
import './styleNew.css'
import { gsap } from 'gsap'
const sliderData = [
{
id: '1',
headerText: `I'm the first Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#500033',
sliderIllustration: '#FF0077',
sliderInner: 'rgba(255, 0, 119, 0.4)',
sliderButton: '#FF0077',
},
},
{
id: '2',
headerText: `I'm the second Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#000050',
sliderIllustration: '#0033FF',
sliderInner: 'rgba(0, 51, 255, 0.4)',
sliderButton: '#0033FF',
},
},
{
id: '3',
headerText: `I'm the third Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#00501D',
sliderIllustration: '#00FF44',
sliderInner: 'rgba(0, 255, 68, 0.4)',
sliderButton: '#00FF44',
},
},
{
id: '4',
headerText: `I'm the fourth Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#554D00',
sliderIllustration: '#FF4E00',
sliderInner: 'rgba(255, 78, 0, 0.4)',
sliderButton: '#FF4E00',
},
},
{
id: '5',
headerText: `I'm the fifth Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#300050',
sliderIllustration: '#8000FF',
sliderInner: 'rgba(128, 0, 255, 0.4)',
sliderButton: '#8000FF',
},
},
{
id: '6',
headerText: `I'm the sixth Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#000050',
sliderIllustration: '#0033FF',
sliderInner: 'rgba(0, 51, 255, 0.4)',
sliderButton: '#0033FF',
},
},
{
id: '7',
headerText: `I'm the seventh Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#00501D',
sliderIllustration: '#00FF44',
sliderInner: 'rgba(0, 255, 68, 0.4)',
sliderButton: '#00FF44',
},
},
{
id: '8',
headerText: `I'm the eighth Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#554D00',
sliderIllustration: '#FF4E00',
sliderInner: 'rgba(255, 78, 0, 0.4)',
sliderButton: '#FF4E00',
},
},
{
id: '9',
headerText: `I'm the ninth Box`,
paragraphText: `Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer lacinia dui lectus. Donec scelerisque ipsum
diam, ac mattis orci pellentesque eget. `,
buttonText: 'Check Now',
colors: {
sliderBox: '#300050',
sliderIllustration: '#8000FF',
sliderInner: 'rgba(128, 0, 255, 0.4)',
sliderButton: '#8000FF',
},
},
]
export default function SliderNew() {
const slider = useRef(undefined)
const prevButton = useRef(undefined)
const nextButton = useRef(undefined)
const trail = useRef([])
useEffect(() => {
console.log('sliders', slider)
console.log('trail', trail)
console.log('nextButton.current', nextButton.current)
console.log('prevButton.current', prevButton.current)
startGsapAnimations()
}, [])
const startGsapAnimations = () => {
// Transform value
let value = 0
// trail index number
let trailValue = 0
// interval (Duration)
let interval = 10000
let ratio = 100 / sliderData.length
const tl = gsap.timeline({
defaults: { duration: 0.6, ease: 'power2.inOut' },
})
tl.from('.bg', { x: '-100%', opacity: 0 })
.from('p', { opacity: 0 }, '-=0.3')
.from('h1', { opacity: 0, y: '30px' }, '-=0.3')
.from('button', { opacity: 0, y: '-40px' }, '-=0.8')
// function to restart animation
const animate = () => tl.restart()
const slide = (condition) => {
// CLear interval
clearInterval(start)
// update value and trailValue
condition === 'increase' ? initiateINC() : initiateDEC()
// move slide
move(value, trailValue)
// Restart Animation
animate()
// start interal for slides back
start = setInterval(() => slide('increase'), interval)
}
// function for increase(forward, next) configuration
const initiateINC = () => {
// Remove active from all trails
sliderData.forEach((_item, index) =>
trail[index].classList.remove('active'),
)
// increase transform value
// console.log('initialInc~value', value)
// console.log('initialInc~calc', (sliderData.length - 1) * ratio)
// console.log(
// 'initialInc~eq',
// Math.round(value) === Math.round((sliderData.length - 1) * ratio),
// )
Math.round(value) === Math.round((sliderData.length - 1) * ratio)
? (value = 0)
: (value += ratio)
// update trailValue based on value
trailUpdate()
}
// function for decrease(backward, previous) configuration
const initiateDEC = () => {
// Remove active from all trails
sliderData.forEach((_item, index) =>
trail[index].classList.remove('active'),
)
// decrease transform value
Math.round(value) === 0
? (value = (sliderData.length - 1) * ratio)
: (value -= ratio)
// update trailValue based on value
trailUpdate()
}
// function to transform slide
const move = (S, T) => {
// transform slider
slider.current.style.transform = `translateX(-${S}%)`
//add active class to the current trail
console.log('trail', T)
trail[Math.round(T)].classList.add('active')
}
const trailUpdate = () => {
trailValue = value / ratio
console.log('trailUpdate', trailValue)
}
// Start interval for slides
let start = setInterval(() => slide('increase'), interval)
nextButton.current.addEventListener('click', () => slide('increase'))
prevButton.current.addEventListener('click', () => slide('decrease'))
const clickCheck = (e) => {
// CLear interval
clearInterval(start)
// Get selected trail
const check = e.target
// remove active class from all trails
sliderData.forEach((_item, index) => {
trail[index].classList.remove('active')
if (check === trail[index]) {
value = index * ratio
}
})
// add active class
check.classList.add('active')
// update trail based on value
trailUpdate()
// transfrom slide
move(value, trailValue)
// start animation
animate()
// start interval
start = setInterval(() => slide('increase'), interval)
}
// Add function to all trails
sliderData.forEach((_item, index) =>
trail[index].addEventListener('click', (ev) => clickCheck(ev)),
)
}
return (
<>