score:0

Accepted answer

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:

<style
    dangerouslysetinnerhtml={{
        __html: [
        `
            .slider {
                display: flex;
                width: ${sliderdata.length * 100}%; /*(this value in css was static 500%, to rendered only 5 slides)*/
                height: 55rem;
                transition: all 0.25s ease-in;
                transform: translatex(0);
            }

            .trail {
                bottom: 5%;
                left: 50%;
                transform: translatex(-50%);
                width: 60%;
                display: grid;
                grid-template-columns: repeat(${sliderdata.length}, 1fr); /*this value was also static in css to display only 5 bars/pages.*/
                gap: 1rem;
                text-align: center;
                font-size: 1.5rem;
            }`,
        ].join('\n'),
    }}
></style>

moreover, each slide was assigned a static class like box1, box2... etc. therefore these styles need to be rendered for each slide like this:

<style
    dangerouslysetinnerhtml={{
        __html: [
            `.slider .box${data.id} {
                background-color: ${data.colors.sliderbox};
            }
            .slider .box${data.id} .illustration .inner {
                background-color: ${data.colors.sliderillustration};
            }
            .slider .box${data.id} .illustration .inner::after, .slider .box${data.id} .illustration .inner::before {
                background-color: ${data.colors.sliderinner};
            }
            .slider .box${data.id} button {
                background-color: ${data.colors.sliderbutton};
            }`,
        ].join('\n'),
    }}
></style>

(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 (
    <>
      <style
        dangerouslysetinnerhtml={{
          __html: [
            `
              .slider {
                display: flex;
                width: ${sliderdata.length * 100}%;
                height: 55rem;
                transition: all 0.25s ease-in;
                transform: translatex(0);
              }
              
              .trail {
                bottom: 5%;
                left: 50%;
                transform: translatex(-50%);
                width: 60%;
                display: grid;
                grid-template-columns: repeat(${sliderdata.length}, 1fr);
                gap: 1rem;
                text-align: center;
                font-size: 1.5rem;
              }`,
          ].join('\n'),
        }}
      ></style>
      <div classname="container">
        <div classname="slider" ref={slider}>
          {sliderdata.map((item, index) => (
            <slide key={index} data={item} />
          ))}
        </div>
        <svg
          ref={prevbutton}
          compclass="prev"
          comptransform="translate(0 91) rotate(-90)"
        />
        <svg
          ref={nextbutton}
          compclass="next"
          comptransform="translate(56.898) rotate(90)"
        />
        <div classname="trail">
          {sliderdata.map((item, index) => (
            <div
              ref={(ref) => {
                trail[index] = ref
              }}
              key={index}
              classname={index == 0 ? `box${item.id} active` : `box${item.id}`}
            >
              {item.id}
            </div>
          ))}
        </div>
      </div>
    </>
  )
}

function slide(props) {
  const { data } = props
  return (
    <>
      <style
        dangerouslysetinnerhtml={{
          __html: [
            `.slider .box${data.id} {
                background-color: ${data.colors.sliderbox};
              }
              .slider .box${data.id} .illustration .inner {
                background-color: ${data.colors.sliderillustration};
              }
              .slider .box${data.id} .illustration .inner::after, .slider .box${data.id} .illustration .inner::before {
                background-color: ${data.colors.sliderinner};
              }
              .slider .box${data.id} button {
                background-color: ${data.colors.sliderbutton};
              }`,
          ].join('\n'),
        }}
      ></style>
      <div classname={`box${data.id} box`}>
        <div classname="bg"></div>
        <div classname="details">
          <h1>{data.headertext}</h1>
          <p>{data.paragraphtext}</p>
          <button>{data.buttontext}</button>
        </div>

        <div classname="illustration">
          <div classname="inner"></div>
        </div>
      </div>
    </>
  )
}

const svg = react.forwardref((props, ref) => {
  console.log('svg', props)
  return (
    <svg
      ref={ref}
      xmlns="http://www.w3.org/2000/svg"
      width="56.898"
      height="91"
      classname={props.compclass}
      viewbox="0 0 56.898 91"
    >
      <path
        fill="#fff"
        d="m45.5,0,91,56.9,48.452,24.068,0,56.9z"
        transform={props.comptransform}
      ></path>
    </svg>
  )
})

the stylenew.css:

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

html {
  box-sizing: border-box;
  font-family: "roboto", sans-serif;
  font-size: 62.5%;
}
@media only screen and (max-width: 800px) {
  html {
    font-size: 57%;
  }
}

body {
  background-color: #000;
  color: #fff;
  padding: 8rem;
}
@media only screen and (max-width: 1000px) {
  body {
    padding: 0;
  }
}

.container {
  position: relative;
  overflow: hidden;
  border-radius: 5rem;
}
@media only screen and (max-width: 1000px) {
  .container {
    border-radius: 0;
  }
}

@media only screen and (max-width: 1000px) {
  .slider {
    height: 100vh;
  }
}
.slider .box {
  height: 100%;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  align-items: center;
  overflow: hidden;
  position: relative;
}
@media only screen and (max-width: 650px) {
  .slider .box {
    grid-template-columns: 1fr;
    grid-template-rows: repeat(2, 1fr);
  }
}
.slider .box .bg {
  padding: 2rem;
  background-color: rgba(0, 0, 0, 0.2);
  width: 55%;
  transform: skewx(7deg);
  position: absolute;
  height: 100%;
  left: -10%;
  padding-left: 20rem;
  transform-origin: 0 100%;
}
@media only screen and (max-width: 800px) {
  .slider .box .bg {
    width: 65%;
  }
}
@media only screen and (max-width: 650px) {
  .slider .box .bg {
    width: 100%;
    left: 0;
    bottom: 0;
    height: 54%;
    transform: skewx(0deg);
  }
}
.slider .box .bg::before {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
  background-color: inherit;
  pointer-events: none;
  transform: skewx(10deg);
}
@media only screen and (max-width: 650px) {
  .slider .box .bg::before {
    width: 120%;
    bottom: 0;
    transform: skewx(0deg);
  }
}
.slider .box .details {
  padding: 5rem;
  padding-left: 10rem;
  z-index: 100;
  grid-column: 1/span 1;
  grid-row: 1/-1;
}
@media only screen and (max-width: 650px) {
  .slider .box .details {
    grid-row: 2/span 1;
    grid-column: 1/-1;
    text-align: center;
    padding: 2rem;
    transform: translatey(-9rem);
  }
}
.slider .box .details h1 {
  font-size: 3.5rem;
  font-weight: 500;
  margin-bottom: 0.5rem;
}
.slider .box .details p {
  display: inline-block;
  font-size: 1.3rem;
  color: #b5b4b4;
  margin-bottom: 2rem;
  margin-right: 5rem;
}
@media only screen and (max-width: 800px) {
  .slider .box .details p {
    margin-right: 0;
  }
}
.slider .box .details button {
  padding: 1rem 3rem;
  color: #fff;
  border-radius: 2rem;
  outline: none;
  border: none;
  cursor: pointer;
  transition: all 0.3s ease;
}
.slider .box .details button:hover {
  opacity: 0.8;
}
.slider .box .details button:focus {
  outline: none;
  border: none;
}

.slider .illustration {
  grid-column: 2/-1;
  grid-row: 1/-1;
  justify-self: center;
}
@media only screen and (max-width: 650px) {
  .slider .illustration {
    grid-row: 1/span 1;
    grid-column: 1/-1;
    display: flex;
    justify-content: center;
    align-items: center;
  }
}
.slider .illustration div {
  height: 25rem;
  width: 18rem;
  border-radius: 3rem;
  background-color: #ff0077;
  position: relative;
  transform: skewx(-10deg);
}
@media only screen and (max-width: 800px) {
  .slider .illustration div {
    height: 20rem;
    width: 15rem;
  }
}
.slider .illustration div::after, .slider .illustration div::before {
  content: "";
  position: absolute;
  height: 100%;
  width: 100%;
  border-radius: 3rem;
  top: 0;
  left: 0;
}
.slider .illustration div::after {
  transform: translate(4rem, -1rem);
}
.slider .illustration div::before {
  transform: translate(2rem, -2rem);
}

.prev,
.next,
.trail {
  z-index: 10000;
  position: absolute;
}

.prev,
.next {
  width: 4rem;
  cursor: pointer;
  opacity: 0.2;
  transition: all 0.3s ease;
}
@media only screen and (max-width: 650px) {
  .prev,
.next {
    display: none;
  }
}
.prev:hover,
.next:hover {
  opacity: 1;
}

.prev {
  top: 50%;
  left: 2%;
  transform: translatey(-50%);
}

.next {
  top: 50%;
  right: 2%;
  transform: translatey(-50%);
}


@media only screen and (max-width: 650px) {
  .trail {
    width: 90%;
    bottom: 13%;
  }
}
.trail div {
  padding: 2rem;
  border-top: 3px solid #fff;
  cursor: pointer;
  opacity: 0.3;
  transition: all 0.3s ease;
}
.trail div:hover {
  opacity: 0.6;
}
@media only screen and (max-width: 650px) {
  .trail div {
    padding: 1rem;
  }
}

.active {
  opacity: 1 !important;
}

Related Query

More Query from same tag