Jump to content
GreenSock

Leaderboard

Popular Content

Showing content with the highest reputation on 02/09/2023 in all areas

  1. Hello @Romanus These articles should give you an idea for how to approach things with GSAP in React. It's also worth mentioning, that GSAP's .context() will be your best friend when it comes to React. https://greensock.com/docs/v3/GSAP/gsap.context() I'm not the most versed with React, but here is your example with GSAP tweening on the cubes' x-rotation. https://codepen.io/akapowl/pen/VwBJYLw
    4 points
  2. Hey there! So page speed (how long it takes to load a page) and rendering performance are completely different things. Rendering perf is the stuff that's happening as you scroll, as you hover over elements and as things animate. Browsers, especially older browsers and mobile browsers can struggle with too much animation, or animating properties that trigger layout or paint. This is a good overview - https://web.dev/rendering-performance This is good advice on rendering perf too. There's no silver bullet though I'm afraid, making sure you animate performant properties like transforms and opacity, reduce animations on mobile, be careful not to overload the browser with filters and blend modes, add will-change on elements that animate, swop perf-intensive animations over to canvas... there's a lot of little micro adjustments. But no one thing that we can suggest that will definitely do the job. Tweak and test, tweak and test. And test on a variety of devices.
    3 points
  3. being that I just did a lesson on cloning groups in my SVG Animation with GreenSock course, I figured I'd take a stab at this. The top-level approach was: take polygons out of pattern in defs wrap them in a group create a loop-inside-a-loop to clone the initial group and place the clones in a grid animate using same technique as above https://codepen.io/snorkltv/pen/ZEjNvKw?editors=1010 Hope this helps
    3 points
  4. It works great!!! Thank you, if you come from Spain (MAD) you are invited to a beer! Or a coffee, whatever you prefer
    2 points
  5. Thanks @Rodrigo and @rubenmeines was helpful.
    2 points
  6. It seems to run fine over here. The only thing is that animating the width and height of elements is not really performant, you could look in to animating a css clip-path or the scale of something, those are much more performant. I also find line 22 - 34 weird in your JS, why animate .fromTo() the same value? Also, GSAP is really smart that if you put numbers in strings see line 40, it will just fix that for you, but you should make the start and end values the same: width: "0", should be width: "0%", than GSAP knows it should include the % in the tween, but personally I like to stay away from animating width and height where I can. Also something you could look in to is the Flip plugin with this you can animate from and two elements based on classes and Flip will figure out all the calculations needed for that. Are you returning the timeline in your function? If that isn't the issue, please start a new topic with a minimal demo showing the issue. animateSectionText() { ... timeline code... return tl; } Also check out this post if you're looking to improve your GSAP code. Hope it helps and happy tweening!
    2 points
  7. Hi, Cassie used the next release's (3.11.5) beta verions of GSAP, ScrollTrigger and ScrollSmoother, that solves a few issues. Let us know if you need a copy of those for your project. Happy Tweening!
    2 points
  8. Hi, Yep, @rubenmeines is right, that's all it takes. Here is a fork of your codepen: https://codepen.io/GreenSock/pen/VwBJPYB Just a side note ScrollSmoother looks by default for elements with the ids #smooth-wrapper and #smooth-content so when you have this in your HTML: <div id="smooth-wrapper"> <div id="smooth-content"> <!--- ALL YOUR CONTENT HERE ---> </div> </div> There is no need to tell ScrollSmoother about the wrapper and the content, it can be this: ScrollSmoother.create({ smooth: 1, effects: true, }); Hopefully this clear things up. Let us know if you have more questions. Happy Tweening!
    2 points
  9. Hi Andy, If you want to use scrollsmoother you need to register it because it is a separate plugin. Then you need to add 2 divs to your html and tell in javascript that you are using those as the scrolling element. Please check: "https://greensock.com/docs/v3/Plugins/ScrollSmoother". This is the HTML <body> <div id="smooth-wrapper"> <div id="smooth-content"> <!--- ALL YOUR CONTENT HERE ---> </div> </div> <!-- position: fixed elements can go outside ---> </body> And this is the JavaScript: import { gsap } from "gsap"; import { ScrollSmoother } from 'gsap/all'; gsap.registerPlugin(ScrollSmoother); let smoother = ScrollSmoother.create({ wrapper: '#smooth-wrapper', content: '#smooth-content', smooth: 2, }); Is that what you meant? In codepen you can ignore the import and the registerplugin if you have used the traditional starterpack from greenSock: https://codepen.io/GreenSock/pen/aYYOdN
    2 points
  10. To answer my own question... The position, scale and orientation is modified on the SVG object - this is where to look to save and restore the state of the SVG. The scale and position are in the viewBox attribute, orientation in data-svg-origin and transform, for example in the demo shown: <svg id="map" class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="173.169 19.9044 1000 1000" style=""> <title>Demo Square</title> <g id="viewport" data-svg-origin="488.94370908783253 572.0179250471222" transform="matrix(-0.80649,0.59125,-0.59125,-0.80649,1243.48156,500.66229)" style="translate: none; rotate: none; scale: none; transform-origin: 0px 0px 0px;"> ... </svg> Add a function to store these values in session, or elsewhere, for example: // // Update saved map state in session // =========================================================================== function updateCurrentTransformation() { var savedMapState = new Object(); savedMapState.svgViewBox = svg.getAttribute("viewBox"); savedMapState.viewportOrigin = viewport.getAttribute("data-svg-origin"); savedMapState.viewportTransform = viewport.getAttribute("transform"); sessionStorage.setItem("savedMapState", JSON.stringify(savedMapState)); //console.log("VBX: " + savedMapState.svgViewBox + " VPO: " + savedMapState.viewportOrigin + " VPT: " + savedMapState.viewportTransform); } Call this function whenever the map changes - for example at the end of the drag: var draggedSvg = Draggable.create(proxy, { ... onDragEnd: function () { updateCurrentTransformation(); }, }); At page initialisation, read these values and modify the SVG attributes with saved values if any: // Get saved map state - will exist if map moved var savedMapState = JSON.parse(sessionStorage.getItem("savedMapState")); if (savedMapState != null) { //console.log("Session exists") if (savedMapState.svgViewBox != null) { svg.setAttribute("viewBox", savedMapState.svgViewBox); }; if (savedMapState.viewportOrigin != null) { viewport.setAttribute("data-svg-origin", savedMapState.viewportOrigin); }; if (savedMapState.viewportTransform != null) { viewport.setAttribute("transform", savedMapState.viewportTransform); }; } Finally, remove the stored session when required, fore example when the SVG is set to original size etc: // // RESET VIEWPORT // =========================================================================== function resetViewport() { ... // remove saved map sate session values sessionStorage.removeItem("savedMapState"); } A working demo and the code referred to is here
    2 points
  11. Hello @slik I removed the link for your example for now, since you pushed your .npmrc file with your private npm token in it. Please remove that and instead use the gsap-trial package in Codesandbox. When you updated your example, add another post in this thread with the new URL, and I'm sure, once one of the Admins finds the time then, they will come in to help you out. Thanks! https://www.npmjs.com/package/gsap-trial
    2 points
  12. Pinning is a pretty complex procedure and you can't really have nested pins like that. However, you can tap into the pinReparent feature that basically forces the pinned content into the very root <body> while it's pinned: https://codepen.io/GreenSock/pen/vYawqXP Notice that I had to set overflow: hidden on your .text-wrapper element, otherwise the translated element might poke out the bottom and extend the size of that section. It's generally best to avoid pinReparent because it can be a bit expensive performance-wise, but it's the only solution in this case that I can see. Does that help?
    2 points
  13. Nice looking site! It performed fine for me. I doubt the problem has anything to do with the version of GSAP. Performance is a very deep topic, so we can't really provide a free performance audit with a lot of detail, but here are a few quick thoughts: You've got loading="lazy" on some images. Be very careful with that because it can cause layout shift and it also forces network connectivity WHILE you're scrolling which can hurt performance. At the very least I'd recommend setting a specific width/height on those to avoid layout shifts (which can be very expensive) It looks like you've got some CSS animations getting triggered. Be careful with those. Definitely avoid animating anything with CSS that you're also animating with GSAP You seem to have a lot of mouse/pointer events. As you scroll, your mouse goes over lots of stuff, so you're triggering stuff quite a bit (expensive). You've got mix-blend-mode: difference in your CSS on the mouse follower. Be very careful with blend modes like that because they can be quite expensive. Filters are also terrible for performance. None of this has anything to do with GSAP, of course. You might want to try setting will-change: transform on any elements that are likely graphics-rendering-intensive. Sometimes it can help. I hope that gets you going in the right direction. Thanks for being a Club GreenSock member! ๐Ÿ’š
    2 points
  14. I just sent you a private message with instructions about where to get the unrestricted beta files. โœ…
    1 point
  15. I've found that adding a mix-blend-mode: difference to the logo is often 'good enough' (it's what we use on https://www.elegantseagulls.com/) so long as the logo is black/white, other wise, it's going to be a performance suck on each tick/scroll to measure the color of the pixels right behind the logo. This will get even tougher with background-images and gradients. If you just had a dark/light mode colors for the logo, you could add an option in the CMS for each <section /> for logo-overlay and use ScrollTrigger to adjust to that via onEnter/onEnterBack, or you could have scrollTrigger trigger a gsap.getProperty() (https://greensock.com/docs/v3/GSAP/gsap.getProperty()) to get the background-color of the section and adjust according to that. But I'd very much advise against watching every nested element on a page, as that's going to be a performance suck.
    1 point
  16. I can't stand any heat over 32ยบC, so probably I'll go back to Spain during autumn or winter, so I'll take some chocolate y churros at La Puerta del Sol Happy Tweening!
    1 point
  17. Hi, In your jump method use ScrollSmoother's scrollTo method instead of the regular ScrollTo Plugin config: const jumptoSection = () => { const targetJp = timelines[currentIndex].scrollTrigger; smoother.scrollTo(targetJp.end, false); }; You can read more about it here: https://greensock.com/docs/v3/Plugins/ScrollSmoother/scrollTo() Let us know if you have more questions. Happy Tweening!
    1 point
  18. Thanks for your feedback on this! I worked out that the easiest way to do this is to handle it in two separate elements. The first is a 100vh container set to position:absolute and a CSS class on the body to block scrolling. When the user clicks on a button inside this hero element, the element gets hidden by tl.to() and a height of 0, revealing the next page section. The onComplete event then removes the scroll-blocking CSS from the BODY.
    1 point
  19. Thanks for confirming that @Cassie I was afraid that may be the issue. Is there any clever way around this? Seems a shame to have to pick between the transforming ability or the tooltip feature. Though, I can appreciate this is just soemtimes the way it is ๐Ÿ˜…
    1 point
  20. 1 point
  21. Ah, yep. Browser limitation. Unfortunately you can't have fixed children of a transformed container. https://stackoverflow.com/questions/2637058/positions-fixed-doesnt-work-when-using-webkit-transform without GSAP, showing the same result https://codepen.io/GreenSock/pen/mdjZOjj
    1 point
  22. Okay, thanks to all of you for your help ๐Ÿ˜
    1 point
  23. Hiya! Could you show me on this demo what you mean by 'interrupted'? https://codepen.io/GreenSock/pen/mdjZPyq?editors=0011
    1 point
  24. https://codepen.io/GreenSock/pen/YzjoweW?editors=1010 Sure, you needed to define the scroller to be the custom element and also scope your selectors inside the loop rather than targeting the first one every time with document.querySelector Hope this helped!
    1 point
  25. That's because you're creating your loop BEFORE your image loads, and then when your image loads it causes a document reflow and totally changes the sizes of stuff, throwing it all off. So just create your loop AFTER things finish loading. window.addEventListener("load", () => { const loop = horizontalLoop(boxes, {paused: false,repeat: -1,}); });
    1 point
  26. Hi, I tested the preview (https://alev.webflow.io/) and it looks fine on Chrome and Firefox on Ubuntu. The other URL looks terrible but I guess that is related to the live preview or something like that. Honestly I don't know what to tell you besides to try the latest version of GSAP and the plugins (3.11.4) since you're one version behind and perhaps test this beta version of ScrollTrigger 3.11.5: https://assets.codepen.io/16327/ScrollTrigger.min.js If possible see if you can replicate the issue in a minimal demo so we can take a better look at it. Using webflow and the code you pasted is not the best way to find and correct issues. Sorry I can't be of more assistance. Happy Tweening!
    1 point
  27. Thank you for the help akapowl and Rodrigo! Its all working now
    1 point
  28. Yep, @akapowl is right, you need to set your fixed elements outside the smoother container: <div id="smooth-wrapper"> <div id="smooth-content"> <!--- ALL YOUR CONTENT HERE ---> </div> </div> <!-- position: fixed elements can go outside ---> Here is a fork of that codepen example using ScrollSmoother so you can see it in action: https://codepen.io/GreenSock/pen/oNMRdBX Hopefully this clear things up. Let us know if you have more questions. Happy Tweening!
    1 point
  29. Thanks you, it's perfect, it helps me a lot!
    1 point
  30. @Carl is correct, you cannot target individual pieces of a pattern. Also, if you are animating pieces of SVG patterns, the performance is really really bad (from personal experience).
    1 point
  31. Hi @juanadie and welcome to the GreenSock forums! First thanks for being a Club GreenSock member and supporting GreenSock! In this case is better to check the ScrollTrigger start/end points for each timeline and feed that value to the ScrollTo plugin: const timelines = [timeline1, timeline2, timeline3, timeline4, timeline5]; const links = gsap.utils.toArray(".link"); let currentIndex = 0; const gotoSection = () => { const targetSt = timelines[currentIndex].scrollTrigger; gsap.to(window, { scrollTo: { y: targetSt.start, autoKill: false }, duration: 1 }); }; links.forEach((link, i) => { link.addEventListener("click", (e) => { e.preventDefault(); currentIndex = i; gotoSection(); }); }); const prevBtn = document.getElementById("prev"); prevBtn.addEventListener("click", () => { if (!currentIndex) return; currentIndex -= 1; gotoSection(); }); const nextBtn = document.getElementById("next"); nextBtn.addEventListener("click", () => { if (currentIndex === links.length - 1) return; currentIndex += 1; gotoSection(); }); Here is a live example: https://codepen.io/GreenSock/pen/zYLMdwP That example uses the start point of the ScrollTrigger instance so it'll take you to the start of the timeline. If you want to go to the end of the timeline just use this in the gotoSection method: const gotoSection = () => { const targetSt = timelines[currentIndex].scrollTrigger; gsap.to(window, { scrollTo: { y: targetSt.end, autoKill: false }, duration: 1 }); }; I used the start point since it makes more sense to me as a user to go to the start of a section instead of the end when clicking an anchor link. Hopefully this helps. Let us know if you have more questions. Happy Tweening!
    1 point
  32. Hi @Mark Howells-Mead and welcome to the GreenSock forums! That's something you could achieve with ScrollTrigger and Observer: https://codepen.io/GreenSock/pen/ExEOeJQ Unfortunately we don't have the time resources to build fully working demos for our users, so hopefully that codepen is enough to get you started. If you run into any issues or have any GSAP related question, let us know and remember to include a minimal demo that shows the problem you're having. I recommend you to take a look at the docs for more information: https://greensock.com/docs/v3/Plugins/ScrollTrigger https://greensock.com/docs/v3/Plugins/Observer Finally the collection of ScrollTrigger examples can provide some good starting points for your project: https://greensock.com/st-demos Happy Tweening!
    1 point
  33. Thanks Cassie. I agree it is a tricky middle ground, so probably unrealistic. Just wishing! Good luck with your curation work and thanks again.
    1 point
  34. Are you trying to link the playhead directly to the scrollbar so it acts as a scrubber? If so, you'd need to set up a tween that controls the playhead (like by setting the .currentTime) and set that up with a ScrollTrigger that has scrub enabled. Here's a quick demo: https://codepen.io/GreenSock/pen/QWOjqxV?editors=0010
    1 point
  35. What I'm trying to accomplish feels like it should be so easy, but I just can't for the life of me seem to nail it down. I have two images, side-by-side. One image is taller and portrait, the other is smaller and landscape. They both exist in a container of which the height is set to the larger image. I want to pin the smaller image on the right so when I scroll, it scrolls the length of the second image and then they both line up at the bottom. Here's how I have my ScrollTrigger object set up: let brandImageBlock = document.getElementById("brand-two-images"); let brandImagePin = document.querySelector("#brand-image-pin"); ScrollTrigger.create({ trigger: brandImagePin, start: "top 20%", end: "bottom " + brandImageBlock.offsetHeight, pin: brandImagePin, }); Maybe I'm just thinking about the end point all wrong, but the pinned image only seems to scroll for a few pixels before stopping.
    1 point
  36. Hey Colin, Welcome to the GSAP forums! You're on the right track - you need to calculate the difference between the two images and then say 'unpin when we've scrolled ?px down from 20% of the way down the viewport https://codepen.io/GreenSock/pen/gOxKvGQ?editors=1010 If you use functional values and invalidateOnRefresh:true - the px value will be refreshed when the page reloads - just in case your styling changes at different media sizes. Hope this helps and good luck with your project!
    1 point
  37. There's an explanation in the docs about this. I find it easiest to visualize it like this: turn markers on for your ScrollTrigger. Now look at where the "start" and โ€œendโ€ values are - it must squish/stretch the timeline to fit inbetween there perfectly. So let's say your ScrollTrigger's end is 500px below the start and you've got 5 seconds worth of tweens in your timeline - if you just change the durations of the tweens to be 100 times what they were before (SUPER long), it doesn't matter because it still has to move the playhead from the start to the end within 500px. See what I mean? So if you want it to last longer, all you need to do is push your "end" further down on the page. Right now, you've got end: "+=100%" which is basically the height of the viewport. Try something like this: end: "+=5000"
    1 point
ร—