AntoninB Posted November 11, 2021 Share Posted November 11, 2021 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): 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 See the Pen 1e13ae4d1583c9a7157b46b995345872 by osublake (@osublake) on CodePen 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 See the Pen QEdpLe by GreenSock (@GreenSock) on CodePen 1 Link to comment Share on other sites More sharing options...
GreenSock Posted November 11, 2021 Share Posted November 11, 2021 I don't have much time right now to dig into all that, @AntoninB but I'd strongly recommend looking at the Helper Functions page in the docs because there's one there that handles infinite looping on the x-axis and there's even a demo with Draggable hooked up: https://greensock.com/docs/v3/HelperFunctions#loop That error you mentioned kinda sounds like maybe you either forgot to gsap.registerPlugin() before trying to use the plugin or perhaps you've got your scripts loading in the <head> instead of at the bottom of the <body>. I'm totally guessing since you didn't provide a minimal demo. If you still need some help, maybe you could provide a minimal demo in CodeSandbox? 1 Link to comment Share on other sites More sharing options...
AntoninB Posted November 11, 2021 Author Share Posted November 11, 2021 Hey @GreenSock, Thank you so much for the reply. The helper function was indeed very handy, and I am now able to reproduce what I wanted to. Sorry for I didn't show any demo (was struggling to get my React component working with Codepen), but here is now one with CodeSandbox: As you see I was able to get the draggable part working thanks to the helper, but I can't get the infinite looping when the carousel is auto-playing. It only loops about one time and then stops. I tried to pass the repeat config param like so: const loop = horizontalLoop(boxes, { paused: false, draggable: true, repeat: true }); But I don't feel like this config param is used anywhere in the helper. How would I make the carousel loops infinitely ? Link to comment Share on other sites More sharing options...
Cassie Posted November 11, 2021 Share Posted November 11, 2021 Hey @AntoninB I think you're looking for -1 repeat How many times the animation should repeat. So repeat: 1 would play a total of two iterations. Default: 0.repeat: -1 will repeat infinitely. Good luck with your project! ✨ 2 Link to comment Share on other sites More sharing options...
AntoninB Posted November 11, 2021 Author Share Posted November 11, 2021 Hey @Cassie Thank you for your help, repeat: -1 was indeed what I was looking for. I feel stupid not finding it myself... I just have one last question. In the example I provided in the Codesandbox, the horizontalLoop() function is stored in a loop variable: const loop = horizontalLoop(boxes, { paused: false, draggable: true, repeat: true }); But that same loopvariable is used in the horizontalLoop function itself to handle the dragging part (in the onPress function for example). So when I'm moving the horizontalLoop function to an external helper file (or outside the useEffect) like that: https://codesandbox.io/s/sxe1n?file=/src/App.js It's obviously throwing the loop is not defined error I guess I'm missing something about how the loop is reused by the drag functions. Link to comment Share on other sites More sharing options...
Cassie Posted November 11, 2021 Share Posted November 11, 2021 Mmm. I'm not really certain what's going on in your codesandbox (I'm not a react person) But in the helper function Jack put together the horizontalLoop function is returning a GSAP timeline with some additional methods added to it, so it can be controlled with previous and next buttons. Inside the function the progress of the timeline is being accessed so that the timeline can be dragged along from that point. You'll need to make sure you can access the timeline's progress within the function or it won't work. I can't help you more than that though I'm afraid. Link to comment Share on other sites More sharing options...
Cassie Posted November 11, 2021 Share Posted November 11, 2021 Potentially some sort of global context or ref forwarding? (I'm largely just guessing and saying react words here 🙃) Good luck figuring it out! I hope this helped a little at least. 2 Link to comment Share on other sites More sharing options...
AntoninB Posted November 11, 2021 Author Share Posted November 11, 2021 Thanks @Cassie for your guesses, you put me on the track saying I had to use the same timeline to handle my drag (which seems pretty obvious now). Basically I just kept using the same tl instance in the drag part, instead of loop, all in the same horizontalLoop function that I put outside my component to be able to reuse it. It's working this way. Note sure it's the best way to do it, I should be digging more into how Gsap and React work but I had to get this animation done quickly, and it's good now. Thank you ! Link to comment Share on other sites More sharing options...
Skelt Posted July 21, 2022 Share Posted July 21, 2022 Hi, @AntoninB I am a newbie and I don't really understood how you fixed your issue with the loop const error. I have the same use case, are you able to share how to fix it ? Does anyone would be able to help me ? My target is to fix the error on the loop variable which is not recognized because it is declared in the useEffect hook. https://codesandbox.io/s/sxe1n?file=/src/App.js Link to comment Share on other sites More sharing options...
GreenSock Posted July 22, 2022 Share Posted July 22, 2022 That's a React/JS question. I'm not a React guy, but I think you'd just create a useRef() that persists and then reference the .current, like: https://codesandbox.io/s/youthful-bush-s8m8sp 🤷♂️ 1 Link to comment Share on other sites More sharing options...
Skelt Posted July 22, 2022 Share Posted July 22, 2022 @GreenSock Thank you a lot ! 🙏 I feel stupid that I stuck on that... 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now