I'm new to GSAP and I'm currently trying to create an image carousel (a marquee) which is looping infinitely and that would be draggable.
I managed to get the infinite loop part working thanks to this thread:
And especially this pen (edit: I don't why but it's displaying at the end of this post):
https://codepen.io/GreenSock/pen/QEdpLe
I adapted it and in the end I have an infinite carousel looping through 9 pictures and displaying 4 of them on the screen (each takes 25% of the screen).
But now I'm struggling with the draggable part. What I'm trying to do is adding the possibility to drag left/right inside the carousel like in this pen
https://codepen.io/osublake/pen/1e13ae4d1583c9a7157b46b995345872
I'm using Next.js with Typescript and styled-components on this component.
I documented myself on how to use gsap with React and SSR the proper way, but I must admit I'm not getting all of it.
Right now, I juste copy/pasted the code from the above pen for the drag, and it's giving me this error as soon as I start dragging:
TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.
I think it is thrown by this line:
animation.progress(wrapProgress(props("x") / (wrapWidth)))
Espacially the props("x") part.
Any help would be appreciated, and thank you to the people of this forum who already taught me a lot.
Here is a copy/paste from my code, sorry it's a bit a mess, everything is in the useEffect cause I had to get access to the window object to determine my item size base on sceen size:
import React, { useEffect, useRef, useState } from "react"
import styled from "styled-components"
import images from "../../core/application/constants/carousel-pictures/places-bottom-carousel"
import gsap, { Linear } from "gsap"
import Draggable from "gsap/dist/Draggable"
import { breakpoints } from "../../themes/breakpoints"
const GsapCarousel: React.FC = () => {
const container = useRef<HTMLDivElement>()
const NUMBER_OF_CARDS = images.length
const setCardsPosition = (): void => {
gsap.set(".box", {
x: (i) => i * (window.innerWidth / 4)
});
}
useEffect(() => {
gsap.registerPlugin(Draggable)
let q = gsap.utils.selector(container)
const proxy = q(".proxy")
console.log('PROXY', proxy);
console.log(q(".box"));
const cardWidth = window.innerWidth / 4
const snapBox = gsap.utils.snap(cardWidth)
const wrapWidth = NUMBER_OF_CARDS * cardWidth
const wrapProgress = gsap.utils.wrap(0, 1)
const props = gsap.getProperty(proxy)
setCardsPosition()
gsap.to(q(".box"), {
duration: 60,
ease: "none",
x: `+=${(window.innerWidth / 4) * NUMBER_OF_CARDS}`,
modifiers: {
x: gsap.utils.unitize(x => parseFloat(x) % ((window.innerWidth / 4) * NUMBER_OF_CARDS)) //force x value to be between 0 and 500 using modulus
},
repeat: -1
});
const animation = gsap.to(q(".box"), {
duration: 1,
x: "+=" + wrapWidth,
ease: Linear.easeNone,
paused: true,
repeat: -1,
modifiers: {
x: function(x, target) {
x = parseFloat(x) % wrapWidth;
return x + "px";
}
}
}).progress(1 / NUMBER_OF_CARDS);
const updateProgress = () => {
animation.progress(wrapProgress(props("x") / (wrapWidth)))
}
Draggable.create(proxy, {
// type: "x",
trigger: q(".boxes"),
throwProps: true,
onDrag: updateProgress,
onThrowUpdate: updateProgress,
snap: {
x: snapBox
}
});
});
const renderCard = (card, index) => {
return (
<CarouselItem key={index} className="box">
<PlaceText>{card.text}</PlaceText>
<Picture src={card.src}></Picture>
</CarouselItem>
)
}
return (
<div ref={container as React.RefObject<HTMLDivElement>}>
<div className="proxy"></div>
<Wrapper>
<CarouselContainer className="boxes">
{images.map(renderCard)}
</CarouselContainer>
</Wrapper>
</div>
)
}
const Wrapper = styled.div`
width: 100%;
margin: auto;
height: 400px;
overflow: hidden;
position: relative;
margin-top: 200px;
border: 2px solid red;
`
const CarouselContainer = styled.div`
position: relative;
@media ${breakpoints.laptop} {
left: -25%;
}
`
const CarouselItem = styled.div`
width: 100%;
position: absolute;
border: 2px solid black;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
@media ${breakpoints.laptop} {
width: 25%;
}
`
const PlaceText = styled.span`
color: ${({ theme }) => theme.text.color.red};
font-family: ${({ theme }) => theme.text.family.atlanticCruise};
display: inline-block;
font-size: 3rem;
margin-bottom: 2rem;
`
const Picture = styled.img`
border-radius: 30px;
width: 90%;
user-select: none;
user-drag: none;
`
export default GsapCarousel