Jump to content
GreenSock

Search the Community

Showing results for 'overwrite'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

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

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Personal Website


Twitter


CodePen


Company Website


Location


Interests

  1. I'm using GSAP to manipulate an SVG map. See pen, based on @OSUblakes fine work with this pen I'd like to be able to maintain the map's position following a page refresh or ajax call for updated content. The whole SVG content will change although the bounds will not - the change would be in the icons. I've already attempted to save what I think might be useful in sessionStorage but get errors storing the object data. It doesn't help that I'm not a JavaScript man, as might be obvious with the following code that spits out it's dummy at the point of storing object with TypeError: cyclic object value Is what I'm trying to do possible, and if so, how? function initMapAnimation() { //sessionStorage.clear(); console.clear(); gsap.defaults({ ease: "power2.in", duration: .1, overwrite: "auto" }) //gsap.registerPlugin(Draggable, InertiaPlugin); var svg = document.querySelector("#map"); var reset = document.querySelector("#reset"); var pivot = document.querySelector("#pivot"); var proxy = document.createElement("div"); var viewport = document.querySelector("#viewport"); var ptzTrigger = document.querySelector("#site"); //var ptzTrigger = document.querySelectorAll('g:not(.clickable)'); var hasSavedValues = true; var rotateThreshold = 4; var reachedThreshold = false; var point = svg.createSVGPoint(); var startClient = svg.createSVGPoint(); var startGlobal = svg.createSVGPoint(); var viewBox = svg.viewBox.baseVal; var cachedViewBox = { x: viewBox.x, y: viewBox.y, width: viewBox.width, height: viewBox.height }; var zoom = { duration: 0.5, animation: new gsap.timeline(), scaleFactor: 1.6, ease: Power2.easeOut, }; gsap.set(pivot, { scale: 0 }); var resetAnimation = new gsap.timeline(); if (sessionStorage.getItem("resetAnimation") != null) { hasSavedValues = false; resetAnimation = JSON.parse(sessionStorage.getItem("resetAnimation") || "[]"); } var pivotAnimation = gsap.to(pivot, { duration: 0.1, alpha: 1, scale: 1, paused: true, }); var draggedSvg = Draggable.create(proxy, { throwResistance: 3000, minimumMovement: 10, trigger: ptzTrigger, throwProps: true, onPress: selectDraggable, onDrag: updateViewBox, onThrowUpdate: updateViewBox, onClick: function () { console.log("clicked"); }, dragClickables: false, }); if (sessionStorage.getItem("draggedSvg") != null) { hasSavedValues = false; draggedSvg = JSON.parse(sessionStorage.getItem("draggedSvg") || "[]"); } var rotatedSvg = Draggable.create(viewport, { type: "rotation", trigger: ptzTrigger, throwProps: true, liveSnap: true, snap: checkThreshold, onPress: selectDraggable, onClick: function () { console.log("clicked"); }, dragClickables: false, }); if (sessionStorage.getItem("rotatedSvg") != null) { hasSavedValues = false; rotatedSvg = JSON.parse(sessionStorage.getItem("rotatedSvg") || "[]"); } rotatedSvg[0].disable(); reset.addEventListener("click", resetViewport); window.addEventListener("wheel", onWheel, { passive: false }); window.addEventListener("resize", function () { pivotAnimation.reverse(); }); window.addEventListener("contextmenu", function (event) { event.preventDefault(); event.stopPropagation(); return false; }); // // INITIALISE VIEWPORT // =========================================================================== if (hasSavedValues == false) { resetAnimation.to(viewport, { duration: 0, attr: { transform: "matrix(1,0,0,1,0,0)" }, // rotation: "0_short", smoothOrigin: true, svgOrigin: "0 0", ease: Power3.easeOut }, 0); } // // ON WHEEL // =========================================================================== function onWheel(event) { event.preventDefault(); event.stopPropagation(); stopUpdateTimer(); pivotAnimation.reverse(); var normalized; var delta = event.wheelDelta; if (delta) { //console.log("FWD"); normalized = (delta % 120) == 0 ? delta / 120 : delta / 12; } else { //console.log("REV"); delta = event.deltaY || event.detail || 0; normalized = -(delta % 3 ? delta * 10 : delta / 3); } var scaleDelta = normalized > 0 ? 1 / zoom.scaleFactor : zoom.scaleFactor; point.x = event.clientX; point.y = event.clientY; var startPoint = point.matrixTransform(svg.getScreenCTM().inverse()); var fromVars = { duration: zoom.duration, ease: zoom.ease, x: viewBox.x, y: viewBox.y, width: viewBox.width, height: viewBox.height, }; viewBox.x -= (startPoint.x - viewBox.x) * (scaleDelta - 1); viewBox.y -= (startPoint.y - viewBox.y) * (scaleDelta - 1); viewBox.width *= scaleDelta; viewBox.height *= scaleDelta; zoom.animation = gsap.from(viewBox, fromVars); //console.log(fromVars); } // // SELECT DRAGGABLE // =========================================================================== function selectDraggable(event) { stopUpdateTimer(); if (resetAnimation.isActive()) { resetAnimation.kill(); } startClient.x = this.pointerX; startClient.y = this.pointerY; startGlobal = startClient.matrixTransform(svg.getScreenCTM().inverse()); // Right mouse button if (event.button === 2) { reachedThreshold = false; gsap.set(viewport, { svgOrigin: startGlobal.x + " " + startGlobal.y }); gsap.set(pivot, { x: this.pointerX, y: this.pointerY }); draggedSvg[0].disable(); rotatedSvg[0].enable().update().startDrag(event); pivotAnimation.play(0); } else { gsap.set(proxy, { x: this.pointerX, y: this.pointerY }); rotatedSvg[0].disable(); draggedSvg[0].enable().update().startDrag(event); pivotAnimation.reverse(); } sessionStorage.setItem("resetAnimation", JSON.stringify(resetAnimation)); sessionStorage.setItem("draggedSvg", JSON.stringify(draggedSvg)); sessionStorage.setItem("rotatedSvg", JSON.stringify(rotatedSvg)); } // // RESET VIEWPORT // =========================================================================== function resetViewport() { var resetDuration = 1.5; var ease = Power3.easeOut; pivotAnimation.reverse(); if (draggedSvg[0].tween) { draggedSvg[0].tween.kill(); } if (rotatedSvg[0].tween) { rotatedSvg[0].tween.kill(); } console.log(resetAnimation); resetAnimation.clear() .to(viewBox, { duration: resetDuration, x: cachedViewBox.x, y: cachedViewBox.y, width: cachedViewBox.width, height: cachedViewBox.height, ease: ease },) .to(viewport, { duration: resetDuration, attr: { transform: "matrix(1,0,0,1,0,0)" }, // rotation: "0_short", smoothOrigin: true, svgOrigin: "0 0", ease: ease }, 0); startUpdateTimer(); sessionStorage.setItem("resetAnimation", JSON.stringify(resetAnimation)); sessionStorage.setItem("draggedSvg", JSON.stringify(draggedSvg)); sessionStorage.setItem("rotatedSvg", JSON.stringify(rotatedSvg)); } // // CHECK THRESHOLD // =========================================================================== function checkThreshold(value) { if (reachedThreshold) { return value; } var dx = Math.abs(this.pointerX - startClient.x); var dy = Math.abs(this.pointerY - startClient.y); if (dx > rotateThreshold || dy > rotateThreshold || this.isThrowing) { reachedThreshold = true; return value; } return this.rotation; } // // UPDATE VIEWBOX // =========================================================================== function updateViewBox() { if (zoom.animation.isActive()) { return; } point.x = this.x; point.y = this.y; var moveGlobal = point.matrixTransform(svg.getScreenCTM().inverse()); viewBox.x -= (moveGlobal.x - startGlobal.x); viewBox.y -= (moveGlobal.y - startGlobal.y); sessionStorage.setItem("resetAnimation", JSON.stringify(resetAnimation)); sessionStorage.setItem("draggedSvg", JSON.stringify(draggedSvg)); sessionStorage.setItem("rotatedSvg", JSON.stringify(rotatedSvg)); } }
  2. Hi, i'm not exactly sure what effect you are going for. FWIW it seems a bit disorienting to move something down to a y:200 while scrolling up. That aside I would avoid creating 2 timelines initially time that control the same properties of the same thing. To avoid conflicts I would suggest creating these animations fresh when you need them inside your callbacks sort of like: ScrollTrigger.create({ trigger: ".top-page", start: "top-=100 top", end: "top+=200 top", markers: true, onEnter: () => {let tl1 = gsap.timeline({}) .fromTo("header", { y: 0, overwrite: 'auto' },{ duration: 2, y: 200, ease: CustomEase.create("custom", "0.5, 0, 0, 1"), overwrite: 'auto' })}, onLeaveBack: () => {let tl2 = gsap.timeline({}) .to("header", { scale: 1.2, y: 200, }) .set("header", {y: 200,scale: 1,},"hide") .to("header", {opacity:1},"start2") .to(("header"), { y: 0, duration: 1, ease: "power4.out" }, "start2")} }); I would also remove locomotive scroll until you know things are working fine without it. Hopefully this set up will allow you to remove some of redundancy between the 2 animations like tweening and setting y:200 multiple times. If you need more help please try to simplify the animations as much as possible in a fork of the original pen.
  3. Well, as with everything, the devil is in the detail, which is why it is very hard to give general recommendations for a more complex scenario like yours. Since you are concerned about things not working when reloading the page when it's scrolled down, you might want to reconsider the general approach of how you implement the change of the backgroundColor, too, because the way you are doing it in the demos you posted, things will not work as you might intend in that case. One logical problem is the following: You are changing the playstate of pre-built timelines with.to() tweens in callbacks of ScrollTriggers, when the page is loaded at the very bottom, ScrollTrigger will make sure that those callbacks get called. So now you have multiple tweens being called quickly one after the other, which are all tweening on the same property of the same element, so you are creating conflicting tweens. When you scroll back up then, the .to() tween is supposed to be reversed, but it will probably reverse back to the color that it was at the time when that tween was being created - which very likely is not the color you'd expect but some value of a color in between all those colors. Creating your tweens upfront can be quite the tricky scenario to begin with, when you are going to tween on the same property of the same element with multiple different instances. So one way you could prevent all those logical hurdles, would be to create the tweens in the callbacks directly instead of pre-building them. Then you could either use .fromTo() tweens to make sure you always tween from one specific color to another specific color, when the callback runs, or .to() tweens with overwrite set to 'auto' to prevent conflicts I mentioned above. In this pen with the lottie-scrolltriggers handling the pinning themselves, things seem to work fine even if I create those ScrollTriggers before all the lottie-scrolltriggers, but your mileage may vary. https://codepen.io/akapowl/pen/gOjKLqy
  4. Hi Everyone! I have a timeline that plays and reverses based on scroll direction. I also use a mouse enter/leave function to trigger/control the same timeline. When using ScrollSmoother the direction overrides the mouse enter/leave functions (the timeline won't play until the scroll is at rest, even when I trigger mouse enter/leave events). My question: Can I overwrite the scroll control to prioritize the "mouse enter/leave" functions? Thanks, as always.
  5. I saw a few problems: You're setting the width/height in the same gsap.set() call as the transformOrigin, and in this very rare edge case that's actually a problem because it just so happens that in the for...in loop through the properties, transformOrigin happens BEFORE the width/height. So when it tries to calculate the percentage-based origin offsets, your <rect> literally has no width or height at all, thus it gets positioned in its upper left corner. The solution: set the width/height FIRST. You could just separate those out into their own gsap.set() that you put first. You're creating conflicting animations. If you click again before the first set of animations completely finishes their 4 repeats, you'll be creating new ones that are also fighting with the old ones for control of the same elements. Make sure you kill() the old animations before you create new ones. Or you can just leverage the overwrite feature (overwrite: true here). Just so you know, the smoothOrigin does absolutely nothing in this line: gsap.timeline({ smoothOrigin: true, yoyo: false, repeat: 4 }); Timelines don't have a property like that. Maybe you intended to pass that down as a default for all child tweens?: gsap.timeline({ defaults: {smoothOrigin: true}, yoyo: false, repeat: 4 }); https://codepen.io/GreenSock/pen/NWBaLOd?editors=0010 Does that clear things up?
  6. I got animation like in the video attached that is triggered via the code below: // tab is the html div object of the clicked tab button // blue line is the html div object of the line below the buttons gsap.to( blueLine, { duration: 0.5, left: tab.offsetLeft, width: tab.offsetWidth, overwrite: false, lazy: false } ) As you can see on the video it looks like gsap is resetting the "left" property to 0 and animates it from there. Is there a way to not reset the value and instead animate from the current property value? I couldn't find anything about it in the docs or forum. Screen Recording 2023-01-24 at 00.43.10.mov
  7. Hi @Haribo, Yeah I see the issue. It seems that adding overwrite into the onLeave an onLeaveBack scroll tweens seems to fix the problem: onLeave: () => { if (i !== panels.length - 1) { let nextPanelId = `panel-${i + 1}`; gsap.to(window, { overwrite: true, scrollTo: { y: ScrollTrigger.getById(nextPanelId).start + 1, autoKill: false }, }); } }, onLeaveBack: () => { if (i) { let prevPanelId = `panel-${i - 1}`; gsap.to(window, { overwrite: true, scrollTo: { y: ScrollTrigger.getById(prevPanelId).end - 1, }, }); } }, From the DOCS (https://greensock.com/docs/v3/GSAP/Tween/vars) : overwrite If true, all tweens of the same targets will be killed immediately regardless of what properties they affect. If "auto", when the tween renders for the first time it hunt down any conflicts in active animations (animating the same properties of the same targets) and kill only those parts of the other tweens. Non-conflicting parts remain intact. If false, no overwriting strategies will be employed. Default: false. Hopefully this helps. Let us know if you have more questions. Happy Tweening!
  8. Hi @aileen-r and welcome to the GreenSock forums! The reason for the different speed is beacuse you're setting the timescale of each animation on the mouse leave event to be 2 and is never reset to 1, that's why is faster after the first mouse over event. If you want to keep the same speed on both events just remove that and make the duration of the animation half the current time: reactions.forEach(reaction => { const action = gsap.to( reaction, { scale: 1.2, margin: '0 30px 0 20px', duration: 0.25, ease: 'power2.inOut', overwrite: 'true', paused: true } ); reaction.addEventListener("mouseenter", function() { action.play(0); }); reaction.addEventListener("mouseleave", function() { action.reverse(); }); }); Now if you still want the leave animation to be faster, then keep the timescale setter on the mouse leave, but add one to the mouse enter as well: reactions.forEach(reaction => { const action = gsap.to( reaction, { scale: 1.2, margin: '0 30px 0 20px', duration: 0.25, ease: 'power2.inOut', overwrite: 'true', paused: true } ); reaction.addEventListener("mouseenter", function() { action.timeScale(1).play(0); }); reaction.addEventListener("mouseleave", function() { action.timeScale(2).reverse(); }); }); You can learn more about timeScale here: https://greensock.com/docs/v3/GSAP/Tween/timeScale() Finally if you want to prevent the bounce effect in the reactions container, that's a bit more tricky since you need to get the current active element check in the array, see if there are elements before and move all those a specific amount of pixels and do the same for the elements after the active one. Finally when leaving the reactions container tween all the reactions back to their original x position. Avoid using margins for this if you can for that scenario. But I must say as a user I would be totally fine with the way things are, just speed up the animation and keep the timescale, no need to change it IMHO. Hopefully this helps. Let us know if you have more questions. Happy Tweening!
  9. Hi all. I have been tasked my by company to revamp our site. We have designs and a concept for a single-page site that makes use of animations tied to scroll progress. GSAP and Scrolltrigger has been amazing for this so far. At this point I have the full animation orchestrated from the top to the bottom of the page. Scrolling at a reasonable speed results in the desired effect. However scrolling too quickly or jumping to certain points on the page results in elements ending up in places where they shouldn't be. The animation is quite a complex one when all put together. It involves the same elements being animated multiple times, as they move around the page while scrolling. I've divided the single-page into various sections, and have a separate timeline that handles each section. I'm unable to share a full codepen as I'm not allowed to share certain information such as our assets, but I have included code below that I hope adequately shows my approach. I've tried various solutions suggested already on this forum, including: setting "immediateRender" and "overwrite", using only fromTo's, relative vs absolute values. I think that my mistake lies in my approach to the task, I believe I'm not employing the optimal practices in order to complete a complex animation of this nature. useEffect(() => { gsap.registerPlugin(ScrollTrigger); planetFloat(); landingPhase(); discoverPhase(); discussPhase(); decidePhase(); }, []); export const landingPhase = () => { gsap .timeline({ scrollTrigger: { trigger: ".scroll-trigger", start: "10% bottom", end: "+=2000", scrub: 0.25, }, }) // Move landing text off screen .to(".landing-text", { opacity: "-=1", x: "+=300", }) // Bring greatness text onto screen .fromTo( ".greatness-text", { autoAlpha: 0, x: -300 }, { autoAlpha: 1, x: 0, }, ">-50%" ) // Scale up discover system .fromTo( ".discover-system", { opacity: 0, scale: 0 }, { opacity: 1, scale: 1, }, // ">-60%" "<" ) // Telescope ledge rises into view .fromTo( ".telescope", { y: 350 }, { y: 0, }, // ">-60%" "<" ) // Greatness text descends below telescope ledge .to( ".greatness-text", { opacity: "-=1", y: "+=550", }, ">100%" ) // Journey text descends into view .fromTo( ".journey-text", { y: -550, autoAlpha: 0 }, { y: 0, autoAlpha: 1, }, ">-70%" ) // Beginning of system rearrange --> .to( "#moon-container", { x: "-=50", y: "-=100", scale: "-=0.7", overwrite: true }, ">100%" ) .to( "#galaxy-swirl-container", { x: "-=400", y: "+=50", rotate: 7, scale: "+=0.4", overwrite: true }, "<" ) .fromTo("#sun-container", { scale: 0 }, { scale: 0.3 }, "<") .to( "#ringed-planet-container", { x: "-=500", y: "+=100", overwrite: true }, "<" ) .fromTo("#red-planet-container", { scale: 0 }, { scale: 1 }, "<") .fromTo("#darkBlue-planet-container", { scale: 0 }, { scale: 1 }, "<") .to( "#lightBlue-planet-container", { x: "-=160", y: "+=240", scale: "+=0.5", overwrite: true }, "<" ) .to( "#dark-planet-container", { x: "+=50", y: "-=150", scale: "-=0.5", overwrite: true }, "<" ) .to( "#turquoise-planet-container", { x: "-=260", y: "+=60", scale: "+=1.5", overwrite: true }, "<" ) // <-- Ending of system rearrange // Telescope ledge zooms out of view .to( ".telescope", { y: "+=500", x: "-=800", }, "<" ) // Journey text zooms out of view .fromTo( ".journey-text", { y: 0, x: 0, }, { y: 500, x: -800, }, "<" ); }; export const discoverPhase = () => { gsap .timeline({ scrollTrigger: { trigger: ".scroll-trigger", start: "25% bottom", end: "+=2000", scrub: 0.25, }, }) // Phase 1 text moves in from the right .fromTo(".phase1-text", { x: 1500 }, { x: 1200 }, "") // Discover text fades in .fromTo( ".discover-text", { autoAlpha: 0, scale: 2.5 }, { autoAlpha: 1, scale: 2.5 }, "<" ) // Phase 1 text moves over the screen .to(".phase1-text", { x: "-=135%" }, ">100%") // Beginning of system rearrange --> .to( "#galaxy-swirl-container", { rotate: -15, x: "+=300", scale: "-=0.4", overwrite: true }, ">-50%" ) .to( "#sun-container", { x: "+=250", y: "+=25", scale: "-=0.05", overwrite: true }, "<" ) .to("#moon-container", { x: "+=250", overwrite: true }, "<") .to( "#lightBlue-planet-container", { x: "+=200", y: "-=50", scale: "-=0.5", overwrite: true }, "<" ) .to( "#dark-planet-container", { x: "+=200", y: "+=25", scale: "-=0.1", overwrite: true }, "<" ) .to( "#turquoise-planet-container", { x: "+=200", y: "-=75", scale: "-=0.5", overwrite: true, }, "<" ) .to( "#red-planet-container", { x: "+=400", y: "+=50", overwrite: true }, "<" ) .to( "#darkBlue-planet-container", { x: "+=400", y: "+=50", overwrite: true }, "<" ) .to( "#ringed-planet-container", { x: "+=450", scale: "-=0.2", overwrite: true }, "<" ) // <-- Ending of system rearrange // Discover text moves away with system .to( ".discover-text", { rotate: -25, x: "+=450", scale: "-=0.6", opacity: "-=1" }, "<" ) // Curiosity header text slides in .fromTo( ".curiosity-header-text", { x: -700, }, { x: 0 }, "<" ) // Curiosity paragraph 1 text slides in .fromTo( ".curiosity-paragraph1-text", { x: -700, }, { x: 0 }, ">-70%" ) // Curiosity paragraph 2 text slides in .fromTo( ".curiosity-paragraph2-text", { x: -700, }, { x: 0 }, ">-70%" ) // Beginning of system rearrange --> .to( "#lightBlue-planet-container", { x: "-=500", y: "+=500", scale: "+=15", overwrite: true, }, ">200%" ) .to( "#turquoise-planet-container", { x: "-=825", y: "+=30", scale: "+=1.5", overwrite: true, }, "<" ) .to( "#ringed-planet-container", { y: "-=50", x: "-=50", scale: "+=1", overwrite: true, }, "<" ) .to( "#red-planet-container", { x: "-=1500", opacity: "-=1", overwrite: true, }, "<" ) .to( "#darkBlue-planet-container", { x: "-=1500", opacity: "-=1", overwrite: true, }, "<" ) .to( "#dark-planet-container", { x: "-=900", y: "-=150", scale: "+=0.5", overwrite: true, }, "<" ) .to( "#moon-container", { y: "-=0", x: "-=200", scale: "+=0.5", overwrite: true, }, "<" ) .to( "#galaxy-swirl-container", { x: "-=1500", opacity: "-=1", scale: "+=10", overwrite: true, }, "<" ) .to( "#sun-container", { opacity: "-=1", x: "-=1500", y: "-=200", scale: "+=2", overwrite: true, }, "<" ) // <-- Ending of system rearrange // Curiosity header text moves off screen .to( ".curiosity-header-text", { x: "-=700", }, "<" ) // Curiosity paragraph 1 text moves off screen .to( ".curiosity-paragraph1-text", { x: "-=700", }, "<" ) // Curiosity paragraph 2 text moves off screen .to( ".curiosity-paragraph2-text", { x: "-=700", }, "<" ) // Phase 1 text moves off screen .to( ".phase1-text", { x: "-=1000", }, "<" ) // Discuss text fades up and in .fromTo( ".discuss-text", { autoAlpha: 0, scale: 0, y: 500, }, { autoAlpha: 1, scale: 2.5, y: 0, }, ">-50%" ) // Phase 2 texts slides on screen .fromTo( ".phase2-text", { x: -1700, }, { x: -800, }, "<" ); };
  10. Hi In my codepen example, when mouse enters the red area, I start 5 tweens, one on each grey bar in the green area. When mouse leaves the red area, I kill all the tweens. Each bar is supposed to grow/shrink to a random value (height css prop), and then when it's finished, repeat this to another random value. It is working almost correctly. The only thing that bothers me is, when you leave the mouse in for a few iterations, sometimes a tween will randomly change his From value before going to his To value. This is making some of the bars randomly "jumping". I am using repeatRefresh: true, and from what I understand, it is supposed to force the next loop using current values as From values. So why are some of these animations still "jumping"? I also tried to use immediateRender: true and overwrite: true, but it changed nothing. Any idea?
  11. Right here: gsap.utils.toArray("#menu a").forEach(el => { let linkTo = document.querySelector(el.getAttribute("data-link")), st = ScrollTrigger.create({trigger: linkTo, start: "top top"}); // create a ScrollTrigger just to track the location of the linkTo element, including pinning, etc. el.addEventListener("click", event => { event.preventDefault(); // don't let the browser jump to the link gsap.set("body", {overflow: "scroll"}); gsap.to(window, {scrollTo: st.start, overwrite: "auto"}); }); }); Jack is creating a ScrollTrigger instance for each section and then getting the start value of that particular ScrollTrigger instance. Finally it passed that to the ScrollTo plugin which creates the scroll animation. I updated the codepen example in order to close the menu as well: https://codepen.io/GreenSock/pen/MWBvVKa Let us know if you have more questions. Happy Tweening!
  12. The live example you have and the demo you provided don't use either Draggable or the Inertia Plugin, so you can set the draggable option to false: let carousel = buildCarousel(items, { radiusX: 250, radiusY: 210, activeAngle: -90, draggable: false, onClick(element, self) { self.to(element, {duration: 1, ease: "power1.inOut"}, "short"); }, onActivate(element, self) { element.classList.add("active"); }, onDeactivate(element, self) { element.classList.remove("active"); }, // when a drag or animation starts (via the Carousel's to()/next()/previous() methods) onStart(element, self) { gsap.to(descriptions[items.indexOf(element)], {autoAlpha: 0, duration: 0.25, overwrite: "auto"}); }, onStop(element, self) { gsap.to(descriptions[items.indexOf(element)], {autoAlpha: 1, overwrite: "auto"}); } }); If you want to use the example with the red circles you can just offset the start and endpoints by minus 25%: gsap.set(items, { motionPath: { path: circlePath, align: circlePath, alignOrigin: [0.5, 0.5], start: -0.25, end: i => (i / items.length) - 0.25, }, scale: 0.9 }); The rest of the code should remain the same. Yet another option with the latter example (red dots) is to set the start point of the path to the top of the circle. Check this article by @PointC https://www.motiontricks.com/cut-your-path-start-points-in-adobe-illustrator/ Hopefully this helps. Happy Tweening!
  13. Thanks for your further help @Rodrigo, I added overwrite, and it seems that the bubbling is stopped, but it still doesn't work correctly. When I scroll, sometimes it works but sometimes does not. How it doesn't work is that the scroll goes to the next panel and back. https://codepen.io/haruka1234/pen/eYjgGvW?editors=1010
  14. Hi, This should work: const roll1 = roll(".rollingText", {duration: 10}), roll2 = roll(".rollingText02", {duration: 10}, true), scroll = ScrollTrigger.create({ onUpdate(self) { if (self.direction !== direction) { direction *= -1; roll1.timeScale(direction * 3); roll2.timeScale(direction * 3); gsap.to([roll1, roll2], {timeScale: direction, overwrite: true, duration: 1}); } } }); Happy Tweening!
  15. Hi, The issue with overwrites is basically generated when one or more GSAP instances affect the same property (blur filter in this case), on the same element at the same time. Right now you are creating two different animations for each element before even running one of them. On top of that you are not correctly reversing all the animations, for example you move from heading-2 to heading-3. When you enter heading-2 the heading-1 element gets affected by one animation, when you move to heading-3, one heading-1 animation is reversed but as soon as you enter heading-3 another one starts while the other is still reversing. You have two different animations affecting the same property on the same element. On top of that overwrite won't help you in this case, you can test it by using this configuration in your methods: function animation_blur(elem) { const theAnimation = gsap.timeline(); gsap.set(elem, { filter: 'blur(0px)' }); theAnimation.to(elem, { filter: 'blur(5PX)', ease: 'none', duration: 0.5, overwrite: true, }); return theAnimation } function animation_blur2(elem) { const theAnimation = gsap.timeline(); gsap.set(elem, { filter: 'blur(0px)' }); theAnimation.to(elem, { filter: 'blur(20PX)', ease: 'none', duration: 0.5, overwrite: true, }); return theAnimation } You'll notice that this definitely doesn't work the way you want. Here is what overwrite does: If true, all tweens of the same targets will be killed immediately regardless of what properties they affect. If "auto", when the tween renders for the first time it hunt down any conflicts in active animations (animating the same properties of the same targets) and kill only those parts of the other tweens. Non-conflicting parts remain intact. If false, no overwriting strategies will be employed. Default: false. Since your animations affect just one property everything will be flushed out and only the animations created by the animation_blur2 method will remain and work, that's why I think the best approach is the one I suggested, as that takes the element from it's current state to the state you want (no blur, low blur or high blur). If you need to use reverse, that means that you are using an event handler for doing that, so why using a method that creates a new GSAP instance is not an option? If you want to keep the current approach you have, you have to look into reversing everything and then playing the instances you want, but you already ran into some logic issues with your code. Based on the Codepen example you gave us I provided a solution for the issue I saw there which should be scalable into a larger setup without too much problems. If you keep having issues, let us know. Happy Tweening!
  16. Hey @Rodrigo - thanks for your response! I appreciate the new approach that you suggested, but I am looking for a solution that still uses .reverse() on mouseleave. (My original codepen is kind of a boiled down example of something more complicated that I'm working on). You mentioned that I'm running into "overwrite issues" - do you think that this is the core issue that I'm facing in my original codepen? Could you possibly expand on overwrite issues? Is there any documentation on overwrite issues? Any ways to combat overwrite issues from occurring? Thanks again!
  17. Hi @Fabian W and welcome to the GreenSock forums! First thanks for being a Club GreenSock member and supporting GSAP! 🥳 What you are looking for is the invalidateOnRefresh configuration option in the ScrollTrigger configuration object: Boolean - If true, the animation associated with the ScrollTrigger will have its invalidate() method called whenever a refresh() occurs (typically on resize). This flushes out any internally-recorded starting values. https://greensock.com/docs/v3/GSAP/Tween/invalidate() This seems to work the way you intend: let t1 = gsap.to(c1, { scrollTrigger: { scrub: true, start: () => 0, end: () => 'max', markers: true, invalidateOnRefresh: true,// <- HERE }, y: () => document.body.clientHeight - c1.clientHeight, ease: 'none', overwrite: 'auto' }); let t2 = gsap.to(c2, { scrollTrigger: { scrub: true, start: () => 0, end: () => 'max', markers: true, invalidateOnRefresh: true,// <- HERE }, y: () => document.body.clientHeight - c2.clientHeight, ease: 'none', overwrite: 'auto' }); Hopefully this helps. If you have more questions let us know. Happy Tweening!
  18. Hi everyones I got stuck at the rotation so my question is how can i Overwrite the rotation ? ?? please go to the Codepen dont know only show the mobile version ... thanks for your time !
  19. Your overall timeline has a scrub: true, this makes it animate on the users scroll position, but you overwrite this scrub (TIL you can overwrite ScrollTrigger properties within a tween!? Edit: No you can't!) within the frame animation and set it to 0.5, this makes it lag behind 0.5 seconds, which will result in it still playing when the next animation is already in view. You could do a few things to resolve this: Set the scrub the same for all animations on the timeline. Have a delay of 0.5 on the next animation either with the position parameter or a delay: 0.5 on the tween Hope it helps and happy tweening!
  20. Hi, OK, thanks for that information, I can see the confusion now. There is no issue at all here, everything is working as expected. The difference between desktop and a touch device are these event handlers: container.addEventListener("mouseenter", () => { reversedOnPause = tl.timeScale() < 0; isOver = true; gsap.to(tl, {timeScale: 0, duration: 1, overwrite: true}); container.classList.add("paused"); }); container.addEventListener("mouseleave", () => { isOver = false; gsap.to(tl, {timeScale: reversedOnPause ? -1 : 1, duration: 1, overwrite: true}); container.classList.remove("paused"); }); So when there is a touch start event in a device it acts as a mouse enter, but the touch end doesn't act like a mouse leave, you actually have to touch elsewhere, outside the boundaries of the container element, to trigger the mouse leave. If you remove those event listeners or add a different logic for touch events it should work as expected. Happy Tweening!
  21. Hey, folks! My team over at The DataFace has this nifty Svelte action powered by GSAP that we've been using for the past few projects. It blends a few approaches that we've seen in the forums. I thought we'd share the wealth with a few key examples in this repl. We have a more complex version that handles timeline positioning that I can share once it's refined. import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger' gsap.registerPlugin(ScrollTrigger); export default (node, { type, children, scrollTrigger, ...args }) => { let targets = children ? node.children : node; let timelineArgs = scrollTrigger ? { scrollTrigger: { trigger: node, start: 'top center', ...scrollTrigger } } : {}; let timeline = gsap.timeline(timelineArgs)[type](targets, { ease: 'power2.out', overwrite: true, ...args }); return { update(params) { timeline.duration(params.duration); }, destroy() { timeline.killTweensOf(targets); } } };
  22. Hi @dhdbtkd welcome to the forum! By default ScrollTrigger selects the the body (or HTML) as the scroll element, eg the element that normally scrolls. If you want to overwrite that, you'll have to define what element is the scroller in your case scroller: ".myScroller", for it to work. (I gave your element a class of myScroller) String | Element - By default, the scroller is the viewport itself, but if you'd like to add a ScrollTrigger to a scrollable <div>, for example, just define that as the scroller. You can use selector text like "#elementID" or the element itself. Read more on the docs https://greensock.com/docs/v3/Plugins/ScrollTrigger https://codepen.io/mvaneijgen/pen/JjZEyVo Personally I wouldn't overwrite the normal browser behavior if you don't have a clear use case for it. In your case you can just set the header and footer to position: fixed; and have the browser behave like it normally does, but that is just my opinion. Hope it helps and happy tweening!
  23. Hi, The issue is not really about overwritting, that's a completely different situation. Basically what's happening here is that you have different event handlers battling for control to play/reverse the same GSAP instance, nothing more. A simple boolean and some conditional logic seems to solve the issue, in order to prevent ScrollTrigger's update callback from controlling the instance: https://codepen.io/GreenSock/pen/Yzvdpog Here you can read more about overwrite: https://greensock.com/docs/v3/GSAP/Tween/vars Let us know if you have more questions. Happy Tweening!
  24. If you want it to happen for each of your images, then set the ScrollTrigger up in the forEach loop over the images. Adjust its start so it is the same as the start for the ScrollTrigger that is scrubbing the image-height plus half a window's height. Set your colors-array up, so that it also contains the initial color as the first value and then target the colors like this in your callbacks. // pseudo-code... const colors = [firstPanelColor, secondPanelColor, thirdPanelColor, fourthPanelColor ] onEnter: () => { gsap.to("body", { duration: 0.5, backgroundColor: colors[i+1], overwrite: 'auto' }); } onLeaveBack: () => { gsap.to("body", { duration: 0.5, backgroundColor: colors[i], overwrite: 'auto' }); } https://codepen.io/akapowl/pen/rNKORBb
  25. it seems I didn’t quite understand you, or I didn’t explain it well, I know how to change the background when the first panel reaches 50%, but how to change the background of the next ones so that each panel has its own color, I tried to do this, but the panel changes color only 1 time per black const colors = ['#F5EBFF','#EEF8FF', '#000000'] ScrollTrigger.create({ trigger: "section.black", scroller: ".scroller", start: () => "top -" + (window.innerHeight * 0.5), onEnter: () => { console.log('color', colors[i]) gsap.to("body", { duration: 1, backgroundColor: colors[i], overwrite: 'auto' }); }, onLeaveBack: () => { gsap.to("body", { duration: 1, backgroundColor: i === 0 ? '#ffffff' : colors[i-1], overwrite: 'auto' }); }, invalidateOnRefresh: true })
×