score:0
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;
}
Source: stackoverflow.com
Related Query
- Recreating slider with gsap and react
- Deleting and recreating an element with React
- Control the material-ui slider with a play and stop buttons in React JS
- Convert this Responsive Blog Card Slider made using HTML, CSS and Swiper js into React js with Swiper js
- Toggling between an image grid and image slider with one array of images in react hooks
- React with GSAP and ScrollMagic - Tween is not valid
- React Nouislider - Issues with onChange, and setState, slider resets to 0, and no value in state
- Simple font size changer with Material UI slider and React
- Use gsap ScrollTrigger with React useRef() and Typescript - types not matching
- How to test a className with the Jest and React testing library
- Add Favicon with React and Webpack
- SyntaxError with Jest and React and importing CSS files
- How to import CSS modules with Typescript, React and Webpack
- How to mock React component methods with jest and enzyme
- How to set up Babel 6 stage 0 with React and Webpack
- Form validation with react and material-ui
- Formatting code with <pre> tag in React and JSX
- Multiple classNames with CSS Modules and React
- React img tag issue with url and class
- Using onBlur with JSX and React
- How do I store JWT and send them with every request using react
- Async validation with Formik, Yup and React
- How to create forms with React and Rails?
- React - useRef with TypeScript and functional component
- Proper way to navigate with React Native, Redux, and Navigator
- Test setting text value with React and Enzyme
- How to make react router work with static assets, html5 mode, history API and nested routes?
- Typescript and React setting initial state with empty typed array
- Cannot read property '.then' of undefined when testing async action creators with redux and react
- Is nesting React Context Provider and consuming those with useContext a problem?
More Query from same tag
- react CSS transitions not firing
- React - How to push array in object using useState
- Update UI within .map() when condition changes?
- Semantic UI doesn't apply the theme specified in theme.config
- ReactJS: Multiple Sort in Table
- React counter hook doesn't increment
- Simulate change for textarea Jest test
- Material UI Button contained style not working with Transition
- Rotate image with respect to arrow key react js
- Cross origin error thrown on GraphQL subscription
- Making webpack 5 asset loader work with webpack-dev-server
- Export "MyModule" is not defined - on Jest tests
- Recharts: No display when looping twice inside the BarChart
- How to perform Firestore validation in functions and send result back to client?
- How to update a single key value pair in an array of objects in react?
- Getting React Developer Tools to Work with Webpack
- JS: Access nested array by key
- Render React Component on Button Click Event
- Change background image on React component hover
- this.props Not Returning Fetched Data From Parent Component (React)
- How test input onChange event with enzyme?
- React.js how to send array to react router
- Trying to create a controlled switcher button
- React state created with useState doesn't update automatically when my API changes
- Promise not returning route
- Proper Way to merge new props into state
- React library problem with importing CSS stylesheet
- ReactJS Website Re-Rendering and Flashing on Mobile Browser
- Show/Hide Input field on checkbox onchange
- Reactjs firing AJAX call multiple times a button is clicked (only want 1 per click!)