pietM Posted July 19, 2021 Share Posted July 19, 2021 Hi everyone, I have a simple nested accordion almost behaving as planned. My question is how to achieve the "slide up" animation. Because the class is set to "display:none;" I understand that I won't see the animation as written, but is there an alternative way to handle the class with GSAP to achieve the slide up animation? Any advice is greatly appreciated. Thanks for all! See the Pen eYWRjXx by evryali (@evryali) on CodePen Link to comment Share on other sites More sharing options...
Cassie Posted July 19, 2021 Share Posted July 19, 2021 Hi @pietM I would probably create a timeline animation which I could play() on opening and reverse() on closing, then use an onReverseComplete callback to remove the class. Lots of different ways to approach this but central to the issue would be not to make the elements display none until you've completed an exit animation. Does that help? Link to comment Share on other sites More sharing options...
OSUblake Posted July 19, 2021 Share Posted July 19, 2021 Here's a demo accordion. See the Pen RwVgEgZ by GreenSock (@GreenSock) on CodePen Based on techniques from this thread. 5 Link to comment Share on other sites More sharing options...
ZachSaucier Posted July 22, 2021 Share Posted July 22, 2021 This came up on StackOverflow. Here's another version of Blake's pen which might be helpful for future readers: See the Pen gOWGKQZ by ZachSaucier (@ZachSaucier) on CodePen 4 Link to comment Share on other sites More sharing options...
OSUblake Posted July 22, 2021 Share Posted July 22, 2021 3 hours ago, ZachSaucier said: This came up on StackOverflow. Here's another version of Blake's pen which might be helpful for future readers: Welp. I thought I gave pretty good instructions in the original thread. A little old, but still relevant. How to toggle tweens in a DRY fashion - GSAP - GreenSock Guess it's time for @Carl make some videos on it. 2 Link to comment Share on other sites More sharing options...
Carl Posted July 23, 2021 Share Posted July 23, 2021 18 hours ago, OSUblake said: Guess it's time for @Carl make some videos on it. 😁 I actually favorited this thread when I saw the accordion demo for "future lesson inspiration" Truth be told, on initial study the code confused me (again). animations.forEach(animate => animate(selected)) using map to create an array of functions and then looping through those functions passing in targets... for some reason my brain just doesn't work that way. I had to re-read your "DRY" post carefully and slowly for it to actually click. I would be more naturally inclined to build something like this closer to what @ZachSaucier provided. The fact that I find the Blake approach so foreign probably means more folks would find it interesting and could benefit from it. 👍 Link to comment Share on other sites More sharing options...
OSUblake Posted July 23, 2021 Share Posted July 23, 2021 1 hour ago, Carl said: The fact that I find the Blake approach so foreign probably means more folks would find it interesting and could benefit from it. 👍 What's foreign? map? map is just a cleaner way to do some loop operations. Whatever you return will be added to a new array. // for loop let animations = []; for (let i = 0; i < myElements.length; i++) { let animaiton = gsap.to(myElemnts[i], { x: 100 }); animations.push(animation); } // forEach let animations = []; myElements.forEach(element => { let animaiton = gsap.to(element, { x: 100 }); animations.push(animation); }); // map let animations = myElements.map(element => { let animation = gsap.to(element, { x: 100 }); return animation; }); // even shorter map let animations = myElements.map(element => gsap.to(element, { x: 100 })); Would have it been clearer if I added some superfluous code? let animations = groups.map(element => createAnimation(element)); 1 hour ago, Carl said: an array of functions and then looping through those functions passing in targets Returning a function was all that was need for the first couple of demos in that thread. In some of the later demos, I start returning an API to handle more complicated situations, like toggling even odd. API? Just a fancy of way of saying a way to interact with something. let myApi = { foo(value) { console.log("FOO", value); }, bar(value) { console.log("BAR", value) } }; myApi.foo("hello"); // FOO hello myApi.bar(34); // BAR 34 That's why I would highly recommend playing around with game programming to anyone who wants to get good at UI. Games are really just super complicated UIs. It forces you to rethink how you approach problems, and group related operations up into an API. 2 Link to comment Share on other sites More sharing options...
OSUblake Posted July 23, 2021 Share Posted July 23, 2021 So here's my demo unrolled. Does it make sense now? See the Pen rNmYMvP by GreenSock (@GreenSock) on CodePen 2 Link to comment Share on other sites More sharing options...
Carl Posted July 23, 2021 Share Posted July 23, 2021 @OSUblake Thanks for all the extra effort and explaining. I get what map() does, but don't use it often (or at all). Probably the biggest hangup is really just the concept of returning functions. It's not something I see often in "user code" around here (or other tutorial sites) although I know it's the secret sauce to many of GSAP's features and helpers. Thanks again. I'll be sure to keep this thread handy. 1 Link to comment Share on other sites More sharing options...
OSUblake Posted July 23, 2021 Share Posted July 23, 2021 It's really no different than calling a function that returns an animation. We're just customizing it. let toggle = createAnimation(); toggle(); function createAnimation() { let animation = gsap.to(".box", { x: 200 }); return function() { animation.reversed(!animation.reversed()); } } Pretty much the same thing as this. let toggle; createAnimation(); toggle(); function createAnimation() { let animation = gsap.to(".box", { x: 200 }); toggle = function() { animation.reversed(!animation.reversed()); } } But maybe the function closure is confusing people. I know some people think that after calling a function that everything inside just gets killed off, which it doesn't. We use closures all the time, like with event listeners, but we usually don't expose the callback to the outside... or do we? createAnimation(); // toggles box animation 😲 box.click(); function createAnimation() { let animation = gsap.to(box, { x: 200 }); box.addEventListener("click", () => { animation.reversed(!animation.reversed()); }); } See the Pen VwbrbXM by GreenSock (@GreenSock) on CodePen 3 Link to comment Share on other sites More sharing options...
Carl Posted July 23, 2021 Share Posted July 23, 2021 stop trying to learn me stuff great demos. they help a lot. 2 Link to comment Share on other sites More sharing options...
superbenj Posted April 25, 2022 Share Posted April 25, 2022 But on this demo, what is the right way to animate another element? like the minus appears when the menu is clicked and the plus disappears? See the Pen RwxmOvQ by superbenj (@superbenj) on CodePen Link to comment Share on other sites More sharing options...
PointC Posted April 25, 2022 Share Posted April 25, 2022 38 minutes ago, superbenj said: But on this demo, what is the right way to animate another element? like the minus appears when the menu is clicked and the plus disappears? You'd create a timeline instead of a single tween. Something like this should work. See the Pen zYpQVxj by PointC (@PointC) on CodePen Happy tweening. 8 Link to comment Share on other sites More sharing options...
Marcel93 Posted January 19 Share Posted January 19 Hi there Thanks a lot! Love that Accordion solution a lot. But how to update the height of the content box on window resize? Best, Marcel Link to comment Share on other sites More sharing options...
Rodrigo Posted January 19 Share Posted January 19 Hi @Marcel93 and welcome to the GSAP Forums! Just use height: "auto" in your GSAP Tween config: See the Pen WNmjYYM by GreenSock (@GreenSock) on CodePen Hopefully this helps. Happy Tweening! 1 Link to comment Share on other sites More sharing options...
Marcel93 Posted January 24 Share Posted January 24 Hi @Rodrigo, thanks a lot I guess my animation works a bit different than the main example. So i have to rebuild it like this that the first child is set to active before using the accordion. And also i try to set it that is used the "height: auto" option and not a fixed px value. This is my code: let groups = gsap.utils.toArray(".accordion-group"); let menus = gsap.utils.toArray(".accordion-menu"); let menuToggles = groups.map(createAnimation); menus.forEach((menu) => { menu.addEventListener("click", () => toggleMenu(menu)); }); function toggleMenu(clickedMenu) { menuToggles.forEach((toggleFn) => toggleFn(clickedMenu)); } function createAnimation(element) { let menu = element.querySelector(".accordion-menu"); let box = element.querySelector(".accordeon-item .accordeon-item__content"); let toggleIcon = element.querySelector(".accordeon-item .accordeon-item__button .button-icon"); gsap.set(box, { height: "auto" }); let animation = gsap .timeline() .from(box, { height: 0, duration: 0.5, ease: "power1.inOut", onComplete: function() { $(toggleIcon).addClass('icon-switch-on'); $(toggleIcon).removeClass('active'); } }) .reverse(); return function (clickedMenu) { if (clickedMenu === menu) { animation.reversed(!animation.reversed()); $(toggleIcon).removeClass('icon-switch-on'); $(toggleIcon).addClass('active'); } else { animation.reverse(); $(toggleIcon).removeClass('icon-switch-on'); $(toggleIcon).addClass('active'); } }; } Thank youu! Link to comment Share on other sites More sharing options...
GSAP Helper Posted January 24 Share Posted January 24 Hi @Marcel93, Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependancies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer. See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo: Using a framework/library like React, Vue, Next, etc.? CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: React (please read this article!) Next Svelte Sveltekit Vue Nuxt Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. ✅ Link to comment Share on other sites More sharing options...
Marcel93 Posted January 24 Share Posted January 24 Hi @Rodrigo, so its basically this example: See the Pen zYpQVxj by PointC (@PointC) on CodePen but i have to make it work responsive and that the first child is already open, so active! I am very new to the timeline functions tbh! THANKS Link to comment Share on other sites More sharing options...
Rodrigo Posted January 24 Share Posted January 24 Hi, I found this demo: See the Pen JjeNMKd by GreenSock (@GreenSock) on CodePen Here is an updated fork: See the Pen BabwZog by GreenSock (@GreenSock) on CodePen That should provide a good starting point. Hopefully this helps. Happy Tweening! 1 Link to comment Share on other sites More sharing options...
Marcel93 Posted January 24 Share Posted January 24 Hi @Rodrigo, thank you a lot That code is also way better! Nice work thank you There is one small problem left. I have different accordion groups... and of each group the first child has to be open. How can i do that? Best, Marcel Link to comment Share on other sites More sharing options...
PointC Posted January 24 Share Posted January 24 Just FYI - the demo of mine you posted can open the first panel automatically by adding this to the end of the code. toggleMenu(menus[0]); Though I'd probably just use the code @Rodrigo posted. See the Pen e5bd6227d11542f55fec6013c8272a4c by PointC (@PointC) on CodePen 1 Link to comment Share on other sites More sharing options...
Rodrigo Posted January 24 Share Posted January 24 50 minutes ago, Marcel93 said: I have different accordion groups... and of each group the first child has to be open. How can i do that? That's just a Javscript logic issue, not really a GSAP related one. Just run the same code for each accordion and it should be fine. Just create a different variable to store each accordion's active element, which can be done with a loop since each loop iteration will have it's own execution context: https://www.freecodecamp.org/news/how-javascript-works-behind-the-scene-javascript-execution-context/ Happy Tweening! 1 Link to comment Share on other sites More sharing options...
Marcel93 Posted January 24 Share Posted January 24 Hi @Rodrigo, thank you! I will take a closer look on this! Very last issue: How can i open and close the Accordion only on the title? So no click effect when i click on the child elements (Because i have a video in the accordion to click on). Best, Marcel Link to comment Share on other sites More sharing options...
Rodrigo Posted January 24 Share Posted January 24 Just move the click event to the title element instead the whole element: items.forEach((e, i) => { const content = e.querySelector(".content"); const t = gsap.to(content, { height: "auto", paused: i ? true : false }); e._accordionTween = t; i === 0 && e.classList.toggle("active"); // Get the title element const title = e.querySelector(".title") // Set the click event on the title title.addEventListener("click", () => { if (currentItem !== null) { items[currentItem].classList.toggle("active"); if (currentItem === i) { currentItem = null; return t.reverse(); } items[currentItem]._accordionTween.reverse(); } e.classList.toggle("active"); t.play(); currentItem = i; }); }); Hopefully this helps. Happy Tweening! 1 Link to comment Share on other sites More sharing options...
Marcel93 Posted January 27 Share Posted January 27 Hi @Rodrigo ! Amazing, thank you 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