Jump to content

Search the Community

Showing results for 'overwrite'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • GreenSock Forums
    • GSAP
    • Banner Animation
    • Jobs & Freelance
  • Flash / ActionScript Archive
    • GSAP (Flash)
    • Loading (Flash)
    • TransformManager (Flash)

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge


  • Learning Center
  • Blog


  • Products
  • Plugins


  • Examples
  • Showcase


  • FAQ


  • ScrollTrigger Demos


There are no results to display.

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start



Personal Website



Company Website



  1. You could wrap your rotatingText in another element and animate that element on scroll, then with the ScrollTrigger onEnter and onLeave callbacks pause the initial tween and play it again. It is a bit hard to give advies when there is no minimal demo in which we can poke around, so this is the best I can do for now. The issue you're facing is because two tweens animate the same element and the same property, so you could also look in to overwrite: true on the second tween or instead of a fixed pixel value make it dynamic with `+=${360 * 5}`, but having two distinct elements is the best way to go in my opinion. Hope it helps and happy tweening!
  2. @1N54N3 I'm not seeing any unexpected behavior. It's important that you understand how tweens work... The very first time a tween renders, it records the start and end values internally so that it can very quickly interpolate between them. There are two issues I see in your setup: You're setting overwrite: true on your 2nd tween which will IMMEDIATELY find any tweens of that same target and kill them. So that wipes out your "moveFrom" tween. You initially paused your timeline, so none of the "to" tweens inside of that will render right away. Then, when you click "play", you're pausing the "moveFrom" tween, thus it never had a chance to render even once and record internally the start/end states. So of course your "moveTo" tween runs from the current value (0). All exactly as expected. Then later, if/when you unpause that "moveFrom" tween, it'll record the start/end values at that point. Since it's a "to" tween, it just uses the CURRENT value as the "from". At the point it renders for the very first time then, you've probably got x at 200 from the other tween. Does that clear things up? You're welcome to use .fromTo() tweens if you need total control of both ends (start/end). Or you can force a render of the tweens in the timeline by doing something like timeline.progress(1).progress(0) before pausing anything internally.
  3. I found the solution in other topic gsap.utils.toArray(".cb-tagreel-row2").forEach((line, i) => { const speed = 1; // (in pixels per second) const links = line.querySelectorAll(".cb-tagreel-item2"), tl = horizontalLoop(links, { speed: speed, repeat: -1 }); links.forEach((link) => { link.addEventListener("mouseenter", () => gsap.to(tl, { timeScale: 0, overwrite: true }) ); link.addEventListener("mouseleave", () => gsap.to(tl, { timeScale: 1, overwrite: true }) ); }); }); function horizontalLoop(items, config) { items = gsap.utils.toArray(items); config = config || {}; let tl = gsap.timeline({ repeat: config.repeat, paused: config.paused, defaults: { ease: "none" }, onReverseComplete: () => tl.totalTime(tl.rawTime() + tl.duration() * 100) }), length = items.length, startX = items[0].offsetLeft, times = [], widths = [], xPercents = [], curIndex = 0, pixelsPerSecond = (config.speed || 1) * 100, snap = config.snap === false ? (v) => v : gsap.utils.snap(config.snap || 1), // some browsers shift by a pixel to accommodate flex layouts, so for example if width is 20% the first element's width might be 242px, and the next 243px, alternating back and forth. So we snap to 5 percentage points to make things look more natural totalWidth, curX, distanceToStart, distanceToLoop, item, i; gsap.set(items, { // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. xPercent: (i, el) => { let w = (widths[i] = parseFloat(gsap.getProperty(el, "width", "px"))); xPercents[i] = snap( (parseFloat(gsap.getProperty(el, "x", "px")) / w) * 100 + gsap.getProperty(el, "xPercent") ); return xPercents[i]; } }); gsap.set(items, { x: 0 }); totalWidth = items[length - 1].offsetLeft + (xPercents[length - 1] / 100) * widths[length - 1] - startX + items[length - 1].offsetWidth * gsap.getProperty(items[length - 1], "scaleX") + (parseFloat(config.paddingRight) || 0); for (i = 0; i < length; i++) { item = items[i]; curX = (xPercents[i] / 100) * widths[i]; distanceToStart = item.offsetLeft + curX - startX; distanceToLoop = distanceToStart + widths[i] * gsap.getProperty(item, "scaleX"); tl.to( item, { xPercent: snap(((curX - distanceToLoop) / widths[i]) * 100), duration: distanceToLoop / pixelsPerSecond }, 0 ) .fromTo( item, { xPercent: snap( ((curX - distanceToLoop + totalWidth) / widths[i]) * 100 ) }, { xPercent: xPercents[i], duration: (curX - distanceToLoop + totalWidth - curX) / pixelsPerSecond, immediateRender: false }, distanceToLoop / pixelsPerSecond ) .add("label" + i, distanceToStart / pixelsPerSecond); times[i] = distanceToStart / pixelsPerSecond; } function toIndex(index, vars) { vars = vars || {}; Math.abs(index - curIndex) > length / 2 && (index += index > curIndex ? -length : length); // always go in the shortest direction let newIndex = gsap.utils.wrap(0, length, index), time = times[newIndex]; if (time > tl.time() !== index > curIndex) { // if we're wrapping the timeline's playhead, make the proper adjustments vars.modifiers = { time: gsap.utils.wrap(0, tl.duration()) }; time += tl.duration() * (index > curIndex ? 1 : -1); } curIndex = newIndex; vars.overwrite = true; return tl.tweenTo(time, vars); } tl.next = (vars) => toIndex(curIndex + 1, vars); tl.previous = (vars) => toIndex(curIndex - 1, vars); tl.current = () => curIndex; tl.toIndex = (index, vars) => toIndex(index, vars); tl.times = times; tl.progress(1, true).progress(0, true); // pre-render for performance if (config.reversed) { tl.vars.onReverseComplete(); tl.reverse(); } return tl; }
  4. You can set overwrite: true, in your tween, so that it will overwrite current tweens already playing. https://codepen.io/mvaneijgen/pen/gOZWbJb?editors=0010 I don't know what you mean with the following
  5. Hi @Tony Geek I would then create all the tweens in the callbacks them self. .fromTo() tween are great for this, because then we can make sure the animations always start from the placed we want. You could of course create a function with a tween in there and call that function each time in the callbacks, but this also works. I've set overwrite: true, just as a precaution for when tweens would overlap. Hope it helps and happy tweening! https://codepen.io/mvaneijgen/pen/ZEVpRXY?editors=0011
  6. Hi everyone, I would like to recreate the animation you can see on CodePen. I'm a beginner about gsap so I need your help. My code is on vue.js and and is as follows: <template> <div class="scrollDist"></div> <div class="main"> <svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <mask id="m"> <g class="medi"> <rect fill="#fff" width="100%" height="100vh" y="850" /> <image y="15%" href="../assets/img/big-cloud.svg" width="1900"/> </g> </mask> <image class="sky" href="../assets/img/sky.jpg" width="1900" /> <image class="piccoli" href="../assets/img/piccoli.svg" width="1000" x="40%" /> <image class="small-cloud" href="../assets/img/small-cloud.svg" x="1%" y="17%" width="20%" /> <image class="background" href="../assets/img/background.png" width="1900" /> <image class="medi" href="../assets/img/medi.svg" width="900" x="20%" /> <image class="grandi" href="../assets/img/grandi.svg" width="1000" x="35%" /> <text fill="#fff" x="35%" y="50%">EXPLORE</text> <image class="arrow" x="47%" y="52%" href="../assets/img/arrow-down.svg" width="30" /> <g mask="url(#m)"> <rect fill="#fff" width="100%" height="100%" /> <text x="35%" y="50%" fill="#162a43">FURTHER</text> </g> <rect ref="arrowBtn" width="30" height="100" opacity="0" x="47%" y="52%" style="cursor:pointer"/> </svg> </div> </template> <script setup> import { gsap } from "gsap"; import ScrollToPlugin from "gsap/ScrollToPlugin"; import { onMounted, ref } from 'vue'; gsap.registerPlugin(ScrollToPlugin); gsap.set('.main', {position:'fixed', background:'#fff', width:'100%', height:'100vh', top:0, left:'50%', x:'-50%'}) gsap.set('.scrollDist', {width:'100%', height:'100vh'}) gsap.timeline({scrollTrigger:{trigger:'.scrollDist', start:'top top', end:'bottom bottom', scrub:1}}) .fromTo('.sky', {y:0},{y:-200}, 0) .fromTo('.medi', {y:100},{y:-1000}, 0) .fromTo('.small-cloud', {y:-150},{y:-500}, 0) .fromTo('.piccoli', {y:-10},{y:-100}, 0) .fromTo('.background', {y:-50},{y:-600}, 0) const arrowBtn = ref(null); onMounted(() => { arrowBtn.value.addEventListener('mouseenter', (e) => { gsap.to('.arrow', {y:10, duration:0.8, ease:'back.inOut(3)', overwrite:'auto'}); }); arrowBtn.value.addEventListener('mouseleave', (e) => { gsap.to('.arrow', {y:0, duration:0.5, ease:'power3.out', overwrite:'auto'}); }); arrowBtn.value.addEventListener('click', (e) => { gsap.to(window, {scrollTo:innerHeight, duration:1.5, ease:'power1.inOut'}); }); }); </script> <style> body, html { width: 100%; height: 100vh; background: #ddd; font-size: 99px; text-align: center; } div { position: absolute; } </style> Can anyone help me? Thanks in advance. C.
  7. Sorry for not providing a codepen. I think it is not possible in this case. I would like to ask if can be think of a way to detect the problem by looking at the page, which is this: https://nmiai.e451.net/ The problem is the following. I have several elements with the class "fade". I watch them with an intersection observer that fires a gsap.to() function when they enter the viewport and another gsap.set() function when they exit the viewport. const fades = document.querySelectorAll('.fade'); if (fades) { fades.forEach(function (fade) { fadeOut(fade); }); } // omitting intersection observer for simplicity function loadFades(entries) { entries.forEach((entry) => { if (entry.isIntersecting) { fadeIn(entry.target); } else { fadeOut(entry.target); } }); } function fadeIn(fade) { gsap.to(fade, { y: '0', opacity: '1', duration: '2', overwrite: true, ease: 'power4.out', }); } function fadeOut(fade) { gsap.set(fade, { y: '50px', opacity: '0', overwrite: true, }); } This is the entire code in context: https://github.com/aitormendez/nmiai-website/blob/main/site/web/app/themes/sage/resources/scripts/animateProject.js This works in all cases except for the work page. On this page the duration of the gsap.to() is multiplied. I haven't been able to figure out why. In this video you can see a comparison of how it works on the front page and on the work page. The first case has a correct duration, the second case does not. Why? Any idea will be welcome.
  8. Hey Cassie Thanks for your swift response, per usual. I'm doing great, thanks for asking. As I said, the actual code is a lot more abstract, since the fade-in animations are set in a completely different script, which is loaded on page load. The animations are set through data-attributes on the elements. Much like a scroll animations library like Animate On Scroll. I can't just overwrite or reset the animations, since I don't really know what the animations _are_. I'll have to experiment, and see if I can work the swopScroller function into the animations library. I'll report back.
  9. Heya! So the best approach here is likely just using tweens. Tweens are more dynamic and ideal for stuff like this. They'll take whatever the property value at that particular point in time is, no matter what it is, and tween to the property value you've specified. https://codepen.io/GreenSock/pen/LYXoKGX?editors=0110 Timelines are great for control, but it can get fiddly if you're animating the same elements in multiple timelines. Sometimes using overwrite:auto and immediateRender:false can help get the behaviour you're after, there's also an invalidate() method that you can call on a timeline that flushes out stored starting values, that can also be helpful too. https://codepen.io/GreenSock/pen/OJggZgK But yeah, I'd likely go for tweens for anything more dynamic and event driven! Hope this helps ☺️
  10. Welcome to the GreenSock forum @blueblueblueblue For ScrollTrigger to get new values after invalidating the old you will need to use function based values for what you're tweening on, or else the invalidation on refresh won't do much for you in the first place. x: () => -1 * width, // instead of x: -1 * width, Secondly, you could try changing the ScrollTrigger event that you overwrite your width variable on, from "refreshInit" to "revert". That does seem to work better for me then. https://codepen.io/akapowl/pen/JjevVGG Personally, I don't actually see the need for an extra function to call on resize when you have only a few values that need re-evalution anyway, so I would actually ommit that function altogether in your case and just get the values on the tween itself directly; then you also wouldn't have to worry about any events anymore. I would also suggest moving away from getBoundingClientRect().width for this, because you technically only add one extra step for the browser to get to the value you need (and getBoundinClientRect() values might not always return what you think they do in other scenarios); when you can instead use something like element.scrollWidth instead. I hope that will help get you on the right track. https://codepen.io/akapowl/pen/vYQjMyZ
  11. Because in my case, I want when end scrollTrigger animation of box1, box2 must be next to child2 but currently it is overwrite each other.
  12. Hello there, Marko, welcome to the GSAP forum. Does the codepen show the same issue for you - because for me it doesn't. I'm not familiar with Elementor at all, but your video very much looks like there are conflicting CSS transitions applied to the element that is being changed inline by ScrollTrigger; so when ScrollTrigger changes e.g. the position to/from fixed on the pinned element, it will transition when the inline styles get applied. I'd suggest digging through your CSS and looking for transitions that are being applied by Elementor - and if they might affect any element that is being targeted by GSAP / ScrollTrigger in any way, remove them / overwrite them so they don't have an effect on that element any more. I hope that will help resolve the issue for you.
  13. For the slide I had used a snippet of code of a codepen demo with scrolltrigger and scrollTo: let sections = gsap.utils.toArray(".scroller-sections"), scrollTween; function goToSection(i) { scrollTween = gsap.to(window, { scrollTo: {y: i * window.innerHeight, autoKill: false}, duration: 1, ease: "expo.out", onComplete: () => { scrollTween = null }, overwrite: true }); } sections.forEach((section, i) => { ScrollTrigger.create({ trigger: section, start: "top bottom-=1", end: "bottom top+=1", onToggle: self => self.isActive && !scrollTween && goToSection(i) }); }); the problems are for the animations that are different for each slide and the elements (inside svg) if you see here https://punctum.studio/preview/sitaricerca/ are positioned with transform or matrix and y or x doesn't work well.
  14. What do I mean by the title is something done like this... but this is different because it doesn't have a scrollbar because it relies in scrollObserver...so I don't understand how can I make work the scrollTrigger alongside inside the scrollObserver. https://codesandbox.io/s/nervous-resonance-xpjvdf?file=/src/OshiNoko.jsx You can check in my code inside...which what I am trying to do is that I'm trying to spread the scale:2 in the text that is closing in the middle so it looks cool...and then once it pass by the middle it will slowly scale:1 or lower the scale...The scale should be dependent if it's closing in the middle of the item...but it doesn't plus it calls many times the markers which kinda sucks and also I don't know why when I'm trying to onUp or onDown it seems the text a bit laggy when transition? what I am missing here? This is the codes. const gotoSection = (index, isScrollingDown) => { let target = isScrollingDown ? swipePanels[currentIndex] : swipePanels[index]; let currentYPercent = gsap.getProperty(".list-of-items", "yPercent"); gsap.to(target, { scale: isScrollingDown ? 2 : 1, // duration:1.25, ease: "power2.inOut", scrollTrigger: { trigger: target, scrub: 1, start: "top center", end: "bottom center", markers: true } }); gsap.to(".list-of-items", { yPercent: limitYPercent(currentYPercent, isScrollingDown ? +1 : -1), duration: 0, // overwrite:true, ease: "none" }); currentIndex = index; }; // ScrollTrigger.create({ // trigger:'.wrapper', // scrub:true, // start: "top center", // end: "bottom center", // markers:true, // onEnter:() => { // let target = swipePanels[currentIndex]; // gsap.to(target,{ // scale:2, // }) // }, // onLeave:() => { // let target = swipePanels[currentIndex]; // gsap.to(target,{ // scale:1, // }) // } // }) instanceObserver = ScrollTrigger.observe({ target: ".wrapper", wheelSpeed: -1, onUp: () => { gotoSection(currentIndex + 1, true); }, onDown: () => { gotoSection(currentIndex - 1, false); }, tolerance: 10, preventDefault: true, allowClicks: true });
  15. @GreenSock Thanks, I just use your code and it works. I guess I should use fromTo tween and also overwrite property, because I also have hover animation. Thanks for your help!
  16. Are you saying that the CodeSandbox demo works fine, but it only breaks when you try it in a different project that you can't show us? A few ideas: Make sure you don't have any CSS transitions or CSS animations applied to the element that you're animating with GSAP. Those could be conflicting. If the user might click multiple times quickly, I'd strongly recommend setting something like overwrite: "auto" or overwrite: true on your first tween to make sure any in-progress tweens of that same element are killed to avoid conflicts. You could probably simplify your code a bit into a single tween that has a repeat/yoyo on it: gsap.fromTo(e.currentTarget, {scale: 1}, {scale: 0.75, duration: 0.25, yoyo: true, repeat: 1, overwrite: true}); There are other ways too. https://codepen.io/GreenSock/pen/BaGjRar?editors=0010 If you still need some help, please make sure you provide a minimal demo that clearly illustrates the issue. We'd be happy to take a look at any GSAP-specific questions you may have.
  17. It seems like you're trying to overwrite what ScrollTrigger is already animating. This will not play nice if a user first scrolls then clicks and then goes back to scrolling again. You almost never want to overwrite tweens you're already created, you can only do this if you're a 100% sure of what you're doing. Here is an example from https://greensock.com/st-demos/ using the scrollTo plugin, with this plugin it allows you to scroll to certain parts of your page and thus ScrollTrigger will just animate things like it would also do if a user scrolls, so no need to create multiple animations that do the same thing. You can do this like the demo is showing, or you can use labels in your timeline to navigate to check out https://greensock.com/docs/v3/Plugins/ScrollTrigger/labelToScroll() https://codepen.io/GreenSock/pen/bGexQpq You can add a empty tween to your timeline to have it do nothing x amount of seconds, with something like this, but this will not work in your current setup, because you have 0 duration tweens which all start at the same time. .add(() => {}, "+=5"); // Add pause of 5 seconds If you want 0 duration tweens just use a .set() function or better change the easing of a normal animation ease: "steps(1)" this will make the transition instant, so it will have the effect you're after, but you keep all the control of the timeline. https://greensock.com/docs/v3/Eases
  18. I assume you meant to do something like this?: var items = gsap.utils.toArray(".gallery-item__inner"), cursor = document.querySelector(".me"), xTo = gsap.quickTo(cursor, "x", {duration: 0.3, ease: "power3"}), yTo = gsap.quickTo(cursor, "y", {duration: 0.3, ease: "power3"}); gsap.set(cursor, {xPercent: -50, yPercent: -50, autoAlpha: 1, scale: 0, pointerEvents: "none"}); window.addEventListener("mousemove", (e) => { xTo(e.pageX); yTo(e.pageY); }); items.forEach((item) => { item.addEventListener("mouseenter", () => { gsap.to(cursor, {scale: 1, duration: 0.2, overwrite: "auto"}); }); item.addEventListener("mouseleave", () => { gsap.to(cursor, {scale: 0, duration: 0.01, overwrite: "auto"}); }); }); https://jsfiddle.net/t6byepfa/
  19. @Rodrigo Thank you for this tip. I used the ScrollTrigger Batch and it seems to be working. The final code is something like this useEffect(() => { gsap.set(cardRef.current, { autoAlpha: 0, y: 60 }) const show = (batch) => { gsap.set(batch, { autoAlpha: 0, y: 60 }) batch.forEach((item, i) => { gsap.timeline().to(item, { autoAlpha: 1, y: 0, overwrite: true, duration: 0.9, stagger: 0.2, ease: 'back.out' }) }) } const hide = (batch) => { gsap.set(batch, { autoAlpha: 0, y: 60, overwrite: true }) } const createBatch = (target) => { ScrollTrigger.refresh() ScrollTrigger.batch(target, { onEnter: show, onLeave: hide, onEnterBack: show, onLeaveBack: hide, markers: true, start: 'top 80%', end: '100% -10%' }) } createBatch(cardRef.current) }, [isSuccess, cardRef]) useEffect(() => { isSuccess && ScrollTrigger.refresh(true) }, [isSuccess]) In createBatch(), the ScrollTrigger.refresh() is required for it to work properly otherwise it is just like before. If you see any mistake in above code please tell me. Thank You very much for you help.
  20. Hi I've been struggling with the issue of conflicting timelines/tweens over and over again. Notice how in the example, the "more info" divs are consistently not appearing after resizing the browser window while the animation is playing (you'll have to open the example in a new tab to see the problem). https://codepen.io/ynamite/pen/jOeovBm I can't seem to revert a timeline/tween to it's initial state and reinitializing the timeline/animation after resizing the browser window. I've tried killing and/or clearing a timeline, I've tried spamming `overwrite: true` everywhere, I've tried debouncing the resize event etc. I'm at a wits end ... I've checked the forum but could only find the solutions I've already tried. Neither overwriting nor killing/clearing tweens/timelines seems to do the job, at least not in all cases. Sometimes it works and in other cases it just doesn't. It feels inconsistent and my code is starting to feel quite hacky. I'm probably missing a thing or doing something wrong. The example shown on the following page seems to exhibit the same behaviour I'm experiencing: Once you click on "move back" and then click on "restart" the animation seems to break. At least in the way that I understand it. By clicking "restart" I'd expect the element to move back to the right edge and roll back to the left (like it doesk on page load). Instead it just hangs and rolls on the left. Thanks!
  21. Hi there, I'm using near latest npm greensock (3.11.4), and there seems to be a change in behaviour since version 2.0.1 and I'm not sure how to resolve it. Before I could have 3 items with infinitely repeating tween inside a timeline, but later I want to stop those tweens on a delay (so they don't stop at the same time), but I don't want to pause the timeline... so I'd add my tweens to a timeline like this: ``` // start rows anim this.spinTimeline = new TimelineLite({paused:true}); _.each(this.rows, (row, i) => { var slideTween = new TweenMax(row.sprite, 3, {x: row.offset, ease: Power0.easeNone, repeat: -1 }); this.spinTimeline.add(slideTween , "startRowT" + i, "-=0.1"); }); this.spinTimeline.play(); ``` then later I could stop them animating repeatedly by just calling a new tween on the row.sprite, with a slightly increasing delay on each, and the animations would transition smoothly from repeating x to landing on a specific point on the x axis. ``` stopRows() { _.each(this.rows, (row, i) => { TweenMax.to(row.sprite, 0.75, {x: row.offset, ease: Elastic.easeOut, delay: 0.75 * i, onComplete: this.animComplete.bind(this) }); }); } ``` Now with latest gsap versions I can't figure out how to recreate this. the repeating tween just keeps playing after the stopping tween finishes. If I pause the timeline first, it works but the repeating anims pause immediately.. If I use overwrite: true, then the repeating anims pause immediately (not when the stop anim starts after the delay). If I use timeline.killTweensOf(row.sprite) onStart, then it happens immediately, (not after the delay).. so i can't transition from one tween to the other anymore. My new code looks like this: ``` // start anim const tl = this.tl; this.rows.map((row, i) => { const offsetX = row.container.width / 2; tl.to(row.container, {x: offsetX, duration: 3, ease: 'none', repeat: -1}); }); tl.play(); // stop anim: this.rows.map((row, i) => { const toX = row.stopOffset; gsap.to(row.container, { duration: 1, x: toX , ease: 'elastic.out', delay: 0.75 * i, overwrite: true, // i'd expect this overwrite to happen after the delay, not immediately. onComplete: () => { //this.tl.killTweensOf(row.container); this.state = 'ready'; }, }); }); Edit: I found the problem, I needed to remove the duration from my new version and use t.killTweensOf in the onComplete (the line i had commented!).. that works as I want now!
  22. Hey, I have found a great and straightforward demo on the forum of repeat and reverse animation, which was created by @mikel. Here it is https://codepen.io/mikeK/pen/dyooKjJ But using the latest GSAP version, the reverse animation doesn’t work the same as in the 3.1.1 version. The animation doesn’t stop on mouseleave. My main goal is to have repeated animation on mouseenter. But on mouseleave, I need to reverse only once instead of reversing the whole repeats. I understand I can do that with tweens and overwrite properties on mouseleave, but I'm looking for a better solution with timeline. Can you help me, please? Below is the same animation with the latest GSAP version.
  23. Hi, The issue is that the calculations ScrollTrigger is making here when creating the linkST instances: let menu = gsap.utils.toArray(".nav a"); menu.forEach((a) => { let element = document.querySelector(a.getAttribute("href")), linkST = ScrollTrigger.create({ trigger: element, start: "top top" }); a.addEventListener("click", (e) => { e.preventDefault(); gsap.to(window, { duration: 1, scrollTo: linkST.start, overwrite: "auto" }); }); }); Are being affected by the ScrollTrigger instances created in the panels loop. Since you know that the home section is at the top of the document just use zero there: a.addEventListener("click", (e) => { e.preventDefault(); const target = e.target.getAttribute("href") === "#home" ? 0 : linkST.start; gsap.to(window, { duration: 1, scrollTo: target, overwrite: "auto", }); }); Finally is worth noticing that pinType : "sticky" is not an option. It's either "fixed" or "transform" pinType "fixed" | "transform" - by default, position: fixed is used for pinning only if the scroller is the <body>, otherwise transforms are used (because position: fixed won't work in various nested scenarios), but you can force ScrollTrigger to use position: fixed by setting pinType: "fixed". Typically this isn't necessary or helpful. Beware that if you set the CSS property will-change: transform, browsers treat it just like having a transform applied, breaking position: fixed elements (this is unrelated to ScrollTrigger/GSAP). Hopefully this helps. Happy Tweening!
  24. Hi, I'm trying to figure out how to reposition and scale an element that has been scaled by a previous animation. The previous animation may have been completed and killed at the time the next animation is called. Overwrite appeared to work only if the animation that positioned and scaled the element previously had not been killed. In this example, I'd like to first apply new position and size attributes, and then apply a new scale. I'd like ellipse1 to end up at the position and scale of ellipse2 after the sequence is completed. Is a sequence like this best accomplished by recording several states in a different order with gsap flip, and animating to those states in the order I'd like them to be played? Thanks for your help!
  25. Yeah, you had !important CSS styles that were interfering. Also, you can simplify this: images.forEach((image) => { if (isEnter) { gsap.to(image, { opacity: 1, scale: 1 }); } else { gsap.to(image, { opacity: 0, scale: 0 }); } }); To this: gsap.to(images, { opacity: isEnter ? 1 : 0, scale: isEnter ? 1 : 0 }); You should also make sure to set opacity/scale to 1 right before the flip in case there's a hover animation running and things are partially-tweened. Like if you hover and then almost immediately click the button, the scale might be 0.5 at that point which could contaminate the final state you're flipping to. So do a gsap.set() with overwrite: true to make sure that other tween is killed too. If you click back and forth between the trigger and reverse buttons in @Rodrigo's demo, you'll see some odd behavior because of that. https://codepen.io/GreenSock/pen/dygzdPE?editors=0010