Jump to content
Search Community

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

Found 1,401 results

  1. Hi @LVegetable welcome to the forum! Have you seen our Stackblitz starter templates? It has a boilerplate for all the major frameworks, including next.js In all my time developing websites I've never needed to overwrite the default scroll behaviour there is always a better way, I think. From your description I would also not read anything that needs to overwrite the default scroll behaviour. Your background can just be a fixed element and the other elements can just scroll or animate them with the y property in GSAP. Keep in mind that everything in GSAP is an animation, even things on scroll start out as an animation. Check out this tutorial how to work with ScrollTrigger Personally I always start in codepen and really focus on the logic I need before I bring it over to my framework. It usually takes me around 10 versions to get to a state I am happy with and then it will be trivial to port it over to what ever framework you like, but the web is basic HTML, CSS, JS so if that is solid it will work any where! You can work with React in codepen and you can then set it up like the pen below. But again, personally I would remove all abstractions and just focus on the basics and when that is working your can port it to what ever you like. Hope it helps and happy tweening! https://codepen.io/GreenSock/pen/OJmQvLZ?editors=0010
  2. Hello, I was not able to recreate this issue in codesandbox or stackblitz, but hope you can give some insight based on what I can provide here. I have a Next.js website using the pages router, and I have an issue with a component that has elements rendered dynamically from a script after the component mounts. (It's a social media feed from Curator.io) The posts in the feed have a scrolltrigger applied to them after the feed has loaded. const curatorContainer = useRef() useGSAP(() => { if ( feedLoaded ) { ScrollTrigger.create({ trigger: curatorContainer.current, start: "top bottom-=200px", onEnter: () => { gsap.fromTo('.crt-post-c', { opacity: 0 }, { opacity: 1, duration: 0.75, delay: 0.2, stagger: 0.1, overwrite: 'auto' }) }, onLeaveBack: () => { gsap.to('.crt-post-c', { opacity: 0, duration: 0.5, overwrite: 'auto' }) }, }) } }, { scope: curatorContainer, dependencies: [feedLoaded] }) return ( <div ref={curatorContainer}> ... </div> ) On the first load everything works fine, but if I navigate to another page and back to the page with this component, I get several "Invalid scope" errors in the console (some on a gsap timeline unrelated to the component that seems to cause the errors). I also get the error "GSAP target .crt-post-c not found." even tho this code spicifically checks for any existing .crt-post-c before running. However the animations work without issues, so I just want to know what's causing the errors and if I can get rid of them. There is no errors when using this same gsap implementation on things that aren't dynamically rendered.
  3. Hi, The first issue in your code is that you're using the quick setter on the parent element, that is the element that contains the grid, and not the grid elements so staggering will have no effect whatsoever. Then I fail to see the point of a quick setter if you can achieve the same with just a GSAP Tween that gets overwritten if another is created. Something like this: useGSAP(() => { let proxy = { translate: 0 }, translateSetter = gsap.quickSetter('.video-container', 'y', 'px'), clamp = gsap.utils.clamp(-1000, 1000); ScrollTrigger.create({ onUpdate: (self) => { translateSetter(clamp(self.getVelocity() / -100)); gsap.to('.video-container', { y: 0, duration: 0.2, stagger: 0.05, overwrite: true, }); }, }); }); Here is a fork of your demo with that approach: https://stackblitz.com/edit/react-hnfbhc?file=src%2FApp.js Hopefully this helps. Happy Tweening!
  4. Hello everyone, I'm currently trying to replicate the effect demonstrated in the uploaded GIF. While I've successfully implemented the easing effect, I'm encountering difficulties with the stagger effect. I've experimented with various approaches, with the latest attempt shown below. I can prepare a CodePen example if needed. Perhaps I'm overlooking something simple. Any guidance or suggestions would be greatly appreciated. Thank you! useGSAP(() => { let proxy = { translate: 0 }, translateSetter = gsap.quickSetter(".video-grid-content-container", "translateY", "px"), clamp = gsap.utils.clamp(-40, 40); ScrollTrigger.create({ onUpdate: (self) => { let translate = clamp(self.getVelocity() / -100); if (Math.abs(translate) > Math.abs(proxy.translate)) { proxy.translate = translate; gsap.to(proxy, { translate: 0, duration: 0.4, stagger: { amount: 10, from: "start" }, overwrite: true, onUpdate: () => translateSetter(proxy.translate) }); } } }); });
  5. Man, this seemed extremely simple of a request in my head... but things like this never are simple, are they? Good question, I mean the specifics of how this will function can be planned later on when you guys decide to implement this... but if I had to chime in now on this specific scenario, I'd say there are more than one option: 1) This will do nothing. Because a motionPath in the context of a FLIP, serves only to overwrite an existing animation... so if a Y animation isn't part of the FLIP, then the motionPath will do nothing. OR 2) If it's used in the same way I used my motionPath, it adds a little detour/curve, but the starting point and ending point is still the same. And I love that about FLIP. To give a helpful analogy like @Rodrigo did earlier, I love the fact that I can just let FLIP take the wheel and take me to my destination. But that doesn't mean I don't want to, as the passenger, ask FLIP to sometimes take a different path than the one it chooses long as the path leads to the same destination. I hope this clarifies my request/idea.
  6. Ah, that's because inside the helper function there was a "resize" event handler that was re-initiating things. I just edited the helper function to put it inside a gsap.context() that uses a cleanup function for the "resize" event handler to remove that: https://stackblitz.com/edit/stackblitz-starters-jbsvf4?file=app%2Fhelper.js function horizontalLoop(items, config) { let timeline; items = gsap.utils.toArray(items); config = config || {}; gsap.context(() => { // use a context so that if this is called from within another context or a gsap.matchMedia(), we can perform proper cleanup like the "resize" event handler on the window let onChange = config.onChange, lastIndex = 0, tl = gsap.timeline({repeat: config.repeat, onUpdate: onChange && function() { let i = tl.closestIndex(); if (lastIndex !== i) { lastIndex = i; onChange(items[i], i); } }, paused: config.paused, defaults: {ease: "none"}, onReverseComplete: () => tl.totalTime(tl.rawTime() + tl.duration() * 100)}), length = items.length, startX = items[0].offsetLeft, times = [], widths = [], spaceBefore = [], xPercents = [], curIndex = 0, indexIsDirty = false, center = config.center, 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 timeOffset = 0, container = center === true ? items[0].parentNode : gsap.utils.toArray(center)[0] || items[0].parentNode, totalWidth, getTotalWidth = () => items[length-1].offsetLeft + xPercents[length-1] / 100 * widths[length-1] - startX + spaceBefore[0] + items[length-1].offsetWidth * gsap.getProperty(items[length-1], "scaleX") + (parseFloat(config.paddingRight) || 0), populateWidths = () => { let b1 = container.getBoundingClientRect(), b2; items.forEach((el, i) => { widths[i] = parseFloat(gsap.getProperty(el, "width", "px")); xPercents[i] = snap(parseFloat(gsap.getProperty(el, "x", "px")) / widths[i] * 100 + gsap.getProperty(el, "xPercent")); b2 = el.getBoundingClientRect(); spaceBefore[i] = b2.left - (i ? b1.right : b1.left); b1 = b2; }); gsap.set(items, { // convert "x" to "xPercent" to make things responsive, and populate the widths/xPercents Arrays to make lookups faster. xPercent: i => xPercents[i] }); totalWidth = getTotalWidth(); }, timeWrap, populateOffsets = () => { timeOffset = center ? tl.duration() * (container.offsetWidth / 2) / totalWidth : 0; center && times.forEach((t, i) => { times[i] = timeWrap(tl.labels["label" + i] + tl.duration() * widths[i] / 2 / totalWidth - timeOffset); }); }, getClosest = (values, value, wrap) => { let i = values.length, closest = 1e10, index = 0, d; while (i--) { d = Math.abs(values[i] - value); if (d > wrap / 2) { d = wrap - d; } if (d < closest) { closest = d; index = i; } } return index; }, populateTimeline = () => { let i, item, curX, distanceToStart, distanceToLoop; tl.clear(); for (i = 0; i < length; i++) { item = items[i]; curX = xPercents[i] / 100 * widths[i]; distanceToStart = item.offsetLeft + curX - startX + spaceBefore[0]; 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; } timeWrap = gsap.utils.wrap(0, tl.duration()); }, refresh = (deep) => { let progress = tl.progress(); tl.progress(0, true); populateWidths(); deep && populateTimeline(); populateOffsets(); deep && tl.draggable ? tl.time(times[curIndex], true) : tl.progress(progress, true); }, onResize = () => refresh(true), proxy; gsap.set(items, {x: 0}); populateWidths(); populateTimeline(); populateOffsets(); window.addEventListener("resize", onResize); 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 && index !== curIndex) { // if we're wrapping the timeline's playhead, make the proper adjustments time += tl.duration() * (index > curIndex ? 1 : -1); } if (time < 0 || time > tl.duration()) { vars.modifiers = {time: timeWrap}; } curIndex = newIndex; vars.overwrite = true; gsap.killTweensOf(proxy); return vars.duration === 0 ? tl.time(timeWrap(time)) : tl.tweenTo(time, vars); } tl.toIndex = (index, vars) => toIndex(index, vars); tl.closestIndex = setCurrent => { let index = getClosest(times, tl.time(), tl.duration()); if (setCurrent) { curIndex = index; indexIsDirty = false; } return index; }; tl.current = () => indexIsDirty ? tl.closestIndex(true) : curIndex; tl.next = vars => toIndex(tl.current()+1, vars); tl.previous = vars => toIndex(tl.current()-1, vars); tl.times = times; tl.progress(1, true).progress(0, true); // pre-render for performance if (config.reversed) { tl.vars.onReverseComplete(); tl.reverse(); } if (config.draggable && typeof(Draggable) === "function") { proxy = document.createElement("div") let wrap = gsap.utils.wrap(0, 1), ratio, startProgress, draggable, dragSnap, lastSnap, initChangeX, wasPlaying, align = () => tl.progress(wrap(startProgress + (draggable.startX - draggable.x) * ratio)), syncIndex = () => tl.closestIndex(true); typeof(InertiaPlugin) === "undefined" && console.warn("InertiaPlugin required for momentum-based scrolling and snapping. https://greensock.com/club"); draggable = Draggable.create(proxy, { trigger: items[0].parentNode, type: "x", onPressInit() { let x = this.x; gsap.killTweensOf(tl); wasPlaying = !tl.paused(); tl.pause(); startProgress = tl.progress(); refresh(); ratio = 1 / totalWidth; initChangeX = (startProgress / -ratio) - x; gsap.set(proxy, {x: startProgress / -ratio}); }, onDrag: align, onThrowUpdate: align, overshootTolerance: 0, inertia: true, snap(value) { //note: if the user presses and releases in the middle of a throw, due to the sudden correction of proxy.x in the onPressInit(), the velocity could be very large, throwing off the snap. So sense that condition and adjust for it. We also need to set overshootTolerance to 0 to prevent the inertia from causing it to shoot past and come back if (Math.abs(startProgress / -ratio - this.x) < 10) { return lastSnap + initChangeX } let time = -(value * ratio) * tl.duration(), wrappedTime = timeWrap(time), snapTime = times[getClosest(times, wrappedTime, tl.duration())], dif = snapTime - wrappedTime; Math.abs(dif) > tl.duration() / 2 && (dif += dif < 0 ? tl.duration() : -tl.duration()); lastSnap = (time + dif) / tl.duration() / -ratio; return lastSnap; }, onRelease() { syncIndex(); draggable.isThrowing && (indexIsDirty = true); }, onThrowComplete: () => { syncIndex(); wasPlaying && tl.play(); } })[0]; tl.draggable = draggable; } tl.closestIndex(true); lastIndex = curIndex; onChange && onChange(items[curIndex], curIndex); timeline = tl; return () => window.removeEventListener("resize", onResize); // cleanup }); return timeline; } Is that better?
  7. Hi @Ponnyprisma and welcome to the GSAP Forums! Sorry to hear about the problems but if you can't reproduce it on a codepen demo there is not a lot we can do about it. On top of that the demo you posted shows that this is not a GSAP related problem, but something else in your app is clearly interfering with how things are being done. There are known issues (not a lot) when using some features by Bootstrap 5, manly because it adds scroll-behavior: smooth to your body element, so if you're using Bootstrap 5 you can overwrite that in your own CSS. Sorry I can't be of more assistance. Happy Tweening!
  8. I noticed a problem with the Firefox browser. I have a looping marquee made with the seamless loop helper function and the Observer plugin on my website, it works great on every browser except Firefox. The marquee loops and it can be controlled with the scroll wheel with the help of the Observer plugin. I have hover event listeners to stop the marquee when I'm hovering on it. When the marquee stops I can scroll normally, when I hover out and the marquee resumes, the scrolling goes back to extremely slow again. This only happens on Mozilla Firefox, works great on the other browsers. Here's the code I'm using for the marquee gsap.registerPlugin(Observer); document.addEventListener("DOMContentLoaded", () => { const projectsList = document.querySelector(".horizontal_projects-wrap"); projectsList.addEventListener('pointerenter', pauseLoop); projectsList.addEventListener('pointerleave', playLoop); const names = document.querySelectorAll(".horizontal_project-title") let hovered = false; function pauseLoop () { tl.pause(); loopObserver.disable(); hovered = true; } function playLoop () { tl.play(); loopObserver.enable(); hovered = false; } const tl = horizontalLoop(names, { repeat: -1, }); const loopObserver = Observer.create({ type: 'wheel', onChangeY(self) { let factor = 2; if (self.deltaY < 0) { factor *= -1.4; } else { factor *= 1.4; } gsap.to(tl, { timeScale: factor * 2, duration: .15, }) gsap.to(tl, { timeScale: factor / 2, duration: .15, onComplete: () => { if (factor<0) { gsap.to(tl, { timeScale: 1, duration: 0.1, }) } } }, "+=.1"); } }); }); /* This helper function makes a group of elements animate along the x-axis in a seamless, responsive loop. Features: - Uses xPercent so that even if the widths change (like if the window gets resized), it should still work in most cases. - When each item animates to the left or right enough, it will loop back to the other side - Optionally pass in a config object with values like "speed" (default: 1, which travels at roughly 100 pixels per second), paused (boolean), repeat, reversed, and paddingRight. - The returned timeline will have the following methods added to it: - next() - animates to the next element using a timeline.tweenTo() which it returns. You can pass in a vars object to control duration, easing, etc. - previous() - animates to the previous element using a timeline.tweenTo() which it returns. You can pass in a vars object to control duration, easing, etc. - toIndex() - pass in a zero-based index value of the element that it should animate to, and optionally pass in a vars object to control duration, easing, etc. Always goes in the shortest direction - current() - returns the current index (if an animation is in-progress, it reflects the final index) - times - an Array of the times on the timeline where each element hits the "starting" spot. There's also a label added accordingly, so "label1" is when the 2nd element reaches the start. */ 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; }
  9. I've tested on my Mac and I don't see what you mean, sorry. Your code seems quite inefficient to me. This is a bit better, although it could be further optimized: https://codepen.io/GreenSock/pen/xxeOjLZ?editors=1010 Make sure you overwrite previous tweens so you're not continually creating new conflicting ones that are fighting for the same property. And a modifier is better than an onUpdate for what you're doing. Are you saying that when you scroll all the way to the bottom of the page, you want your x animation to suddenly stop? Does it work the way you want if you REMOVE Lenis? That's not a GreenSock product, so we can't really support that. I think the whole point of Lenis is that it'll smooth the scroll so that it doesn't suddenly stop, so I wonder if what you're asking is more of a Lenis question, not a GSAP one(?)
  10. Hello, I wanted to ask - is it possible to run the from() animation on an element after the to() animation has worked on it? Here's an example, first I run the to() animation: let hideSearchTl = gsap.timeline({ defaults: { ease: 'cubic-1', duration: 0.2, overwrite: true, }, }), elements = $('selector'); hideSearchTl.to(elements, { width: 0, autoAlpha: 0, padding: 0, }); Then, when performing certain actions, I want to return the element with a from() animation: hideSearchTl.from(elements, { width: 0, autoAlpha: 0, padding: 0, }); Using reverse() is not entirely suitable (or I don’t know how to use it) because it “reverses” the easy property. I need easy the same as in to() animation. As far as I understand the complexity in the filled style attribute, I just can’t come up with a solution.
  11. Hi, I am building a slider based on this example https://codepen.io/andrei-savu/pen/BaPqzvX It works when it's alone on a page, https://yaojuilan.art/gsap While it isn't working when there is something else https://yaojuilan.art/system_of_conductors/field-walk#kinmen (the slider works sometime. it is unstable. ) I tried logging out the observer onChange, the event does trigger, but the items just would not do the horizontal transition. I am wondering if observer has some sort of limitation, or maybe observer listener is interfering with something? Sorry i did not create a codepen, because this component does works standalone. Here is the slider component export default async function Page() { const data= await getPageContent() return ( <div id='intro' className='relative h-auto w-full overflow-x-hidden'> <div className='h-[50vh] w-full'> some content </div> <Slider items={data?.carousel_img?.images} /> <div className='h-[200vh] w-full bg-red-100'> some content </div> </div> ) } export default function Slider({ items, section }) { useGSAP(() => { let loop = horizontalLoop(`.carousel-${section} li`, { repeat: -1 }) let slow = gsap.to(loop, { timeScale: 0, duration: 0.5 }) loop?.timeScale(0) Observer.create({ target: `.carousel-${section}`, type: 'pointer,touch,wheel', wheelSpeed: -1, preventDefault: true, onChange: (self) => { loop.timeScale(Math.abs(self.deltaX) > Math.abs(self.deltaY) ? -self.deltaX : -self.deltaY) // whichever direction is bigger slow.invalidate().restart() // now decelerate }, }) }) return ( <div className='absolute bottom-12 w-full cursor-grab overflow-hidden'> <ul className={`carousel-${section} carousel flex flex-nowrap pl-0`}> {items?.map((item, i) => ( <li key={i}> <Image alt={'collective of images'} src={item} width={150} height={150} sizes='100vw' className='pointer-events-none touch-none select-none ' /> </li> ))} </ul> </div> ) } function horizontalLoop(items, config) { items = gsap.utils.toArray(items) if (!items.length) return 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 }
  12. Very thanks, moreover I found that my code has a transition property from default css, I needed to overwrite it
  13. Here are some of the problems I noticed: You were adding a new "mouseleave" event handler on EVERY "mousemove" event, so when the mouse left the button, there were probably thousands of those firing, all trying to tween the same properties of the same element. Massively wasteful. You weren't applying any overwrite logic, so you were creating a bunch of overlapping tweens, all controlling the same stuff. I'd recommend using gsap.quickTo() for the best performance on things where you're constantly interrupting and trying to go to new destinations. You were using pageX/pageY instead of clientX/clientY for measurements, thus after scrolling the calculations would be incorrect. You were only applying the magnetic affect to one .btn instead of all of them. Just loop through them accordingly. You were using an onscroll listener, but you might want to consider using ScrollTrigger instead because it simplifies things. Maybe this will help: https://codepen.io/GreenSock/pen/QWooxbG?editors=0010
  14. Hi @isaac19197 welcome to the forum! It is hard to debug your setup from just a code snippet (also because this is just 1/3 of the setup, CSS and HTML is really important, to get a proper inside of what is going on). It can be fairly difficult to target the same element with different tweens and have them fight for control. There is a few things you can try, set immediateRender: false, to the later tween or overwrite: true. You can also do an onComplete call back on your first tween and only create the ScrollTrigger instance if the original animation has finished playing. These are just some things I can say on top of my head . If you still need help please create a minimal demo, so that we can take a look at your code in action. Side note I see you've found the .to() and .fromTo() tweens, but have you also seen .from()? For instance you can rewrite your frist tween like so, which will do the same as your .fromTo() tween .from(mainText, { opacity: 0, // Will animate from to browser default 1 scale: 0.5, // will animate from to browser default 1 duration: 1 }) Hope it helps and happy tweening! And here a Codepen demo you can just fork which loads all the GSAP plugins https://codepen.io/GreenSock/pen/aYYOdN
  15. Yeah, to avoid that your HTML/CSS needs some basic structure first, then figure a way to animate said structure. Sometimes is necessary to overwrite something set in the CSS files in order to make something work as expected. Finally in over 12 years of doing web development I've never heard of a single person who told me that ran into an user that had JS disabled on the browser. Normal users don't do that, so more than an irrational fear is an unfounded one IMHO. Happy Tweening!
  16. At first glance the rabbit hole did not seem that deep, I might be wrong when I apply it across my entire application (I already notice that I should replace the setTimeouts with a delay for instance). But in short, this is how I approached it. I have made a file that operates as a wrapper for gsap, it imports an external file that manages the state of the animations (a Pinia datastore in my case). Which has a boolean property called "showAnimations" to determine wether animations will be played or not. /services/gsap-wrapper.ts import gsap from "gsap" import appStore from "@/stores/app" const gsapWrapper = {...gsap} gsapWrapper.to = ( target: gsap.TweenTarget, durationOrVars: number | gsap.TweenVars, vars?: gsap.TweenVars ) => { let options = {} as gsap.TweenVars if (typeof durationOrVars === "number") { options.duration = durationOrVars } else { vars = durationOrVars } options = { ...options, ...vars, } as gsap.TweenVars // Pinia datastore const app = appStore() // Overwrite duration & delay when animations are not allowed if (!app.showAnimations) { options.duration = 0 options.delay = 0 options.ease = "none" if (typeof options.stagger == "number") { options.stagger = 0 } else if (typeof options.stagger === "object" && options.stagger !== null) { options.stagger.each = 0 } } return gsap.to(target, options) } gsapWrapper.fromTo = ( target: gsap.TweenTarget, durationOrFromVars: number | gsap.TweenVars, fromOrToVars: gsap.TweenVars, toVars?: gsap.TweenVars ) => { let options = {} as gsap.TweenVars let fromVars = fromOrToVars if (typeof durationOrFromVars === "number") { options.duration = durationOrFromVars fromVars = fromOrToVars } else { toVars = fromOrToVars fromVars = durationOrFromVars } options = { ...options, ...toVars, } as gsap.TweenVars // Pinia datastore const app = appStore() // Overwrite duration & delay when animations are not allowed if (!app.showAnimations) { options.duration = 0 options.delay = 0 options.ease = "none" if (typeof options.stagger == "number") { options.stagger = 0 } else if (typeof options.stagger === "object" && options.stagger !== null) { options.stagger.each = 0 } } return gsap.fromTo(target, { ...fromVars, }, options) } export default gsapWrapper In the other files where I normally include gsap via import gsap from "gsap", I now simply import it as followed: import gsap from "/services/gsap-wrapper" This approach allows me to enable/disable all gsap animations from 1 single location which seem to have the following up- and downsides. Upsides I have on boolean variable that I can modify to disable ALL animations throughout my application The code is relatively simple, and can be easily adjusted and expanded It uses the default gsap library, so unless breaking changes occur in the gsap.to method. Everything will remain working with future gsap updates Downsides It is applicable to ALL gsap.to methods. As a workaround you could import both the gsap-wrapper and gsap into a file, but that could easily become a slippery slope It can be quite a hassle to figure out how the wrapper function should be written to respect the original gsap method and its types
  17. I'm not sure what you mean by "properly" - from what I can tell, it's doing everything exactly properly. Are you trying to find all the tweens that affect a particular target and kill them? If so, you can use gsap.killTweensOf(). If you want to just find the tweens of a particular target, you can use gsap.getTweensOf(). overwrite: "auto" runs the first time the tween renders, and it finds ONLY the in-progress tweens that are affecting the same individual properties of the same target, and kills those (just the individual properties that overlap). If you set overwrite: true, it'll find all of the tweens of the same target(s) regardless of what properties overlap, and kill them immediately. So with the above tools, you should be able to accomplish pretty much anything you need.
  18. Hi Rodrigo, I did some experiments after reading through your response. The animation goes back to normal after the window is resized. However, we can't just ask the visitors to resize the window to see the effect, right? And, I am unsure what you mean by "the order things are called". Assuming I am right, I moved the About section above all other animations. However, the problem still occurs. Then, I try to comment out the command lines one by one. I noticed that as long as the "homePlanet" animation is not calling. Everything will work just fine, as you showed me in your reply. So, I started asking myself about my looping rotation and its speed. Please see my codes below. planet .to(".dot-line", { repeat: -1, rotation: "+=360", duration: 100, ease: "none", transformOrigin: "center center" }) .to(".planet", { repeat: -1, rotation: "+=360", ease: "none", svgOrigin: "431.92 431.92", duration: 30, overwrite: "auto" }, 0) .to(".planet-ab", { repeat: -1, rotation: "+=360", ease: "none", svgOrigin: "566.76 566.76", duration: 60, overwrite: "auto" }, 0) I am using duration to control its speed. I wonder if it could be the duration that caused the issue. This looping feature involved 2 sections, which you can see in the below codepen link. https://codepen.io/danclp/pen/KKEarwe The rotation works as a continuing animation, even after it has been scroll-triggered away in the first section. You can still see the rotation animation available in the second section (the sliding text section). Did I code correctly, or does it have another way of calling when we want it to animate continuously? Also, should I use duration or something else when I want to slow down the rotation speed? Can you give me some advice? Thank you.
  19. juste a self note, this will make tl more hard to manage: example: so i need to alway think to override complexe tl. example here the pivot not work because the last crush the first ! am not sure i understand what happen underhood ! It's a shame not to have an understanding to just overwrite the delays and keep the classic behavior on auto.
  20. ho sorry , oki after many test i just starting found correct solutions: `overwrite: 'auto'` => ` overwrite: true,` https://codepen.io/djmisterjon/pen/BabpgPx?editors=0011 i will need make more deep test to see if this broke behaviors in my app !? but seem promising, de delay animation seem override thanks
  21. 120Tracking Prevention blocked access to storage for <URL>. require.js:168 Uncaught Error: Mismatched anonymous define() module: function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function n(t){return"string"==typeof t}function o(t){return"function"==typeof t}function p(t){return"number"==typeof t}function q(t){return void 0===t}function r(t){return"object"==typeof t}function s(t){return!1!==t}function t(){return"undefined"!=typeof window}function u(t){return o(t)||n(t)}function K(t){return(l=pt(t,at))&&ie}function L(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function M(t,e){return!e&&console.warn(t)}function N(t,e){return t&&(at[t]=e)&&l&&(l[t]=e)||at}function O(){return 0}function Y(t){var e,i,n=t[0];if(r(n)||o(n)||(t=[t]),!(e=(n._gsap||{}).harness)){for(i=dt.length;i--&&!dt[i].targetTest(n););e=dt[i]}for(i=t.length;i--;)t[i]&&(t[i]._gsap||(t[i]._gsap=new Ft(t[i],e)))||t.splice(i,1);return t}function Z(t){return t._gsap||Y(yt(t))[0]._gsap}function $(t,e){var r=t[e];return o(r)?t[e]():q(r)&&t.getAttribute(e)||r}function _(t,e){return(t=t.split(",")).forEach(e)||t}function aa(t){return Math.round(1e5*t)/1e5||0}function ba(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++i<r;);return i<r}function ca(t,e,r){var i,n=p(t[1]),a=(n?2:1)+(e<2?0:1),o=t[a];if(n&&(o.duration=t[1]),o.parent=r,e){for(i=o;r&&!("immediateRender"in i);)i=r.vars.defaults||{},r=s(r.vars.inherit)&&r.parent;o.immediateRender=s(i.immediateRender),e<2?o.runBackwards=1:o.startAt=t[a-1]}return o}function da(){var t,e,r=ot.length,i=ot.slice(0);for(ut={},t=ot.length=0;t<r;t++)(e=i[t])&&e._lazy&&(e.render(e._lazy[0],e._lazy[1],!0)._lazy=0)}function ea(t,e,r,i){ot.length&&da(),t.render(e,r,i),ot.length&&da()}function fa(t){var e=parseFloat(t);return(e||0===e)&&(t+"").match(nt).length<2?e:t}function ga(t){return t}function ha(t,e){for(var r in e)r in t||(t[r]=e[r]);return t}function ia(t,e){for(var r in e)r in t||"duration"===r||"ease"===r||(t[r]=e[r])}function ka(t,e){for(var i in e)t[i]=r(e[i])?ka(t[i]||(t[i]={}),e[i]):e[i];return t}function la(t,e){var r,i={};for(r in t)r in e||(i[r]=t[r]);return i}function ma(t){var e=t.parent||F,r=t.keyframes?ia:ha;if(s(t.inherit))for(;e;)r(t,e.vars.defaults),e=e.parent||e._dp;return t}function pa(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function qa(t,e){!t.parent||e&&!t.parent.autoRemoveChildren||t.parent.remove(t),t._act=0}function ra(t){for(var e=t;e;)e._dirty=1,e=e.parent;return t}function ua(t){return t._repeat?_t(t._tTime,t=t.duration()+t._rDelay)*t:0}function wa(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function xa(t){return t._end=aa(t._start+(t._tDur/Math.abs(t._ts||t._rts||B)||0))}function ya(t,e){var r;if((e._time||e._initted&&!e._dur)&&(r=wa(t.rawTime(),e),(!e._dur||gt(0,e.totalDuration(),r)-e._tTime>B)&&e.render(r,!0)),ra(t)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur<t.duration())for(r=t;r._dp;)0<=r.rawTime()&&r.totalTime(r._tTime),r=r._dp;t._zTime=-B}}function za(t,e,r,i){return e.parent&&qa(e),e._start=aa(r+e._delay),e._end=aa(e._start+(e.totalDuration()/Math.abs(e.timeScale())||0)),function _addLinkedListItem(t,e,r,i,n){void 0===r&&(r="_first"),void 0===i&&(i="_last");var a,s=t[i];if(n)for(a=e[n];s&&s[n]>a;)s=s._prev;s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t}(t,e,"_first","_last",t._sort?"_start":0),t._recent=e,i||ya(t,e),t}function Aa(t,e){return(at.ScrollTrigger||L("scrollTrigger",e))&&at.ScrollTrigger.create(e,t)}function Ba(t,e,r,i){return qt(t,e),t._initted?!r&&t._pt&&(t._dur&&!1!==t.vars.lazy||!t._dur&&t.vars.lazy)&&d!==Mt.frame?(ot.push(t),t._lazy=[e,i],1):void 0:1}function Ea(t,e,r){var i=t._repeat,n=aa(e)||0;return t._dur=n,t._tDur=i?i<0?1e10:aa(n*(i+1)+t._rDelay*i):n,t._time>n&&(t._time=n,t._tTime=Math.min(t._tTime,t._tDur)),r||ra(t.parent),t.parent&&xa(t),t}function Fa(t){return t instanceof Bt?ra(t):Ea(t,t._dur)}function Ha(t,e){var r,i,a=t.labels,s=t._recent||mt,o=t.duration()>=E?s.endTime(!1):t._dur;return n(e)&&(isNaN(e)||e in a)?"<"===(r=e.charAt(0))||">"===r?("<"===r?s._start:s.endTime(0<=s._repeat))+(parseFloat(e.substr(1))||0):(r=e.indexOf("="))<0?(e in a||(a[e]=o),a[e]):(i=+(e.charAt(r-1)+e.substr(r+1)),1<r?Ha(t,e.substr(0,r-1))+i:o+i):null==e?o:+e}function Ia(t,e){return t||0===t?e(t):e}function Ka(t){return(t+"").substr((parseFloat(t)+"").length)}function Na(t,e){return t&&r(t)&&"length"in t&&(!e&&!t.length||t.length-1 in t&&r(t[0]))&&!t.nodeType&&t!==i}function Qa(t){return t.sort(function(){return.5-Math.random()})}function Ra(t){if(o(t))return t;var p=r(t)?t:{each:t},_=zt(p.ease),m=p.from||0,g=parseFloat(p.base)||0,v={},e=0<m&&m<1,y=isNaN(https://requirejs.org/docs/errors.html#mismatch at makeError (require.js:168:17) at intakeDefines (require.js:1254:36) at Object.localRequire [as require] (require.js:1446:21) at requirejs (require.js:1797:24) at custom.js:32:1 makeError @ require.js:168 intakeDefines @ require.js:1254 localRequire @ require.js:1446 requirejs @ require.js:1797 (anonymous) @ custom.js:32 testrahul.js:1 hiiiiiiiiiiiiiiiiiiiiiiiii testrahul.js:42 Service Worker registered with scope: https://totalfood.greenhonchos.in/ totalfood.greenhonchos.in/:1100 [Intervention] Images loaded lazily and replaced with placeholders. Load events are deferred. See https://go.microsoft.com/fwlink/?linkid=2048113 customtest.js:34 gsap test[object Object] customtest.js:35 {"Back":{},"Bounce":{},"CSSPlugin":{"name":"css","aliases":{"autoAlpha":"opacity,visibility","scale":"scaleX,scaleY","alpha":"opacity","transform":"x,y,z,scale,scaleX,scaleY,xPercent,yPercent,rotation,rotationX,rotationY,skewX,skewY","translateX":"x","translateY":"y","translateZ":"z","rotate":"rotation","rotationZ":"rotation","rotateZ":"rotation","rotateX":"rotationX","rotateY":"rotationY"},"core":{}},"Circ":{},"Cubic":{},"Elastic":{},"Expo":{},"Linear":{},"Power0":{},"Power1":{},"Power2":{},"Power3":{},"Power4":{},"Quad":{},"Quart":{},"Quint":{},"Sine":{},"SteppedEase":{},"Strong":{},"default":{"utils":{},"effects":{},"ticker":{"time":0.537,"frame":5,"_listeners":[null]},"plugins":{},"globalTimeline":{"vars":{"sortChildren":false,"defaults":{"duration":0.5,"overwrite":false,"delay":0},"autoRemoveChildren":true,"id":"root","smoothChildTiming":true},"_delay":0,"_repeat":0,"_ts":1,"_dur":0,"_tDur":0,"labels":{},"smoothChildTiming":true,"autoRemoveChildren":true,"_sort":false,"_dirty":0,"_zTime":0.006,"_tTime":0.537,"_time":0.537,"_act":false,"_initted":1},"core":{},"version":"3.3.1"},"gsap":{"utils":{},"effects":{},"ticker":{"time":0.537,"frame":5,"_listeners":[null]},"plugins":{},"globalTimeline":{"vars":{"sortChildren":false,"defaults":{"duration":0.5,"overwrite":false,"delay":0},"autoRemoveChildren":true,"id":"root","smoothChildTiming":true},"_delay":0,"_repeat":0,"_ts":1,"_dur":0,"_tDur":0,"labels":{},"smoothChildTiming":true,"autoRemoveChildren":true,"_sort":false,"_dirty":0,"_zTime":0.006,"_tTime":0.537,"_time":0.537,"_act":false,"_initted":1},"core":{},"version":"3.3.1"}} customtest.js:36 Uncaught TypeError: gsap.to is not a function at customtest.js:36:14 at Object.execCb (require.js:1696:33) at Module.check (require.js:883:51) at Module.<anonymous> (require.js:1139:34) at require.js:134:23 at require.js:1189:21 at each (require.js:59:31) at Module.emit (require.js:1188:17) at Module.check (require.js:938:30) at Module.enable (require.js:1176:22) (anonymous) @ customtest.js:36 execCb @ require.js:1696 check @ require.js:883 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 enable @ require.js:1176 init @ require.js:788 (anonymous) @ require.js:982 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 enable @ require.js:1176 init @ require.js:788 (anonymous) @ require.js:1014 (anonymous) @ require.js:134 runCallbacks @ domReady.js:24 callReady @ domReady.js:35 pageLoaded @ domReady.js:50 setTimeout (async) (anonymous) @ domReady.js:94 execCb @ require.js:1696 check @ require.js:883 enable @ require.js:1176 init @ require.js:788 callGetModule @ require.js:1203 completeLoad @ require.js:1590 onScriptLoad @ require.js:1717 load (async) req.load @ require.js:1942 load @ require.js:1685 load @ require.js:834 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 callPlugin @ require.js:1098 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 (anonymous) @ require.js:1161 (anonymous) @ require.js:134 each @ require.js:59 enable @ require.js:1113 init @ require.js:788 (anonymous) @ require.js:1460 setTimeout (async) req.nextTick @ require.js:1815 localRequire @ require.js:1449 requirejs @ require.js:1797 (anonymous) @ customtest.js:29 current_location_modal.js:11 custom-checkout location/index/index/ current_location_modal.js:21 test modal compat.js:43 Fallback to JQueryUI Compat activated. Your store is missing a dependency for a jQueryUI widget. Identifying and addressing the dependency will drastically improve the performance of your site. (anonymous) @ compat.js:43 execCb @ require.js:1696 check @ require.js:883 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 enable @ require.js:1176 init @ require.js:788 (anonymous) @ require.js:1014 (anonymous) @ require.js:134 (anonymous) @ mixins.js:129 execCb @ require.js:1696 check @ require.js:883 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 enable @ require.js:1176 init @ require.js:788 (anonymous) @ require.js:1014 (anonymous) @ require.js:134 (anonymous) @ mixins.js:129 execCb @ require.js:1696 check @ require.js:883 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 enable @ require.js:1176 init @ require.js:788 (anonymous) @ require.js:1014 (anonymous) @ require.js:134 (anonymous) @ mixins.js:129 execCb @ require.js:1696 check @ require.js:883 (anonymous) @ require.js:1139 (anonymous) @ require.js:134 (anonymous) @ require.js:1189 each @ require.js:59 emit @ require.js:1188 check @ require.js:938 enable @ require.js:1176 init @ require.js:788 callGetModule @ require.js:1203 completeLoad @ require.js:1590 onScriptLoad @ require.js:1717 load (async) req.load @ require.js:1942 load @ require.js:1685 load @ require.js:834 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 (anonymous) @ require.js:1161 (anonymous) @ require.js:134 each @ require.js:59 enable @ require.js:1113 init @ require.js:788 (anonymous) @ require.js:1460 setTimeout (async) req.nextTick @ require.js:1815 localRequire @ require.js:1449 load @ mixins.js:128 (anonymous) @ require.js:1095 (anonymous) @ require.js:134 on @ require.js:517 callPlugin @ require.js:955 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 (anonymous) @ require.js:1161 (anonymous) @ require.js:134 each @ require.js:59 enable @ require.js:1113 init @ require.js:788 callGetModule @ require.js:1203 completeLoad @ require.js:1590 onScriptLoad @ require.js:1717 load (async) req.load @ require.js:1942 load @ require.js:1685 load @ require.js:834 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 (anonymous) @ require.js:1161 (anonymous) @ require.js:134 each @ require.js:59 enable @ require.js:1113 init @ require.js:788 (anonymous) @ require.js:1460 setTimeout (async) req.nextTick @ require.js:1815 localRequire @ require.js:1449 load @ mixins.js:128 (anonymous) @ require.js:1095 (anonymous) @ require.js:134 on @ require.js:517 callPlugin @ require.js:955 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 (anonymous) @ require.js:1161 (anonymous) @ require.js:134 each @ require.js:59 enable @ require.js:1113 init @ require.js:788 callGetModule @ require.js:1203 completeLoad @ require.js:1590 onScriptLoad @ require.js:1717 load (async) req.load @ require.js:1942 load @ require.js:1685 load @ require.js:834 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 (anonymous) @ require.js:1161 (anonymous) @ require.js:134 each @ require.js:59 enable @ require.js:1113 init @ require.js:788 callGetModule @ require.js:1203 completeLoad @ require.js:1590 onScriptLoad @ require.js:1717 load (async) req.load @ require.js:1942 load @ require.js:1685 load @ require.js:834 fetch @ require.js:824 check @ require.js:856 enable @ require.js:1176 enable @ require.js:1557 (anonymous) @ require.js:1161 (anonymous) @ require.js:134 each @ require.js:59 enable @ require.js:1113 init @ require.js:788 (anonymous) @ require.js:1460 setTimeout (async) req.nextTick @ require.js:1815 localRequire @ require.js:1449 requirejs @ require.js:1797 (anonymous) @ (index):418
  22. Hi @adapfityDesigns and welcome to the GSAP Forums! Just use the tickerDirection boolean in your mouseleave event handler to conditionally set the timescale to either 1 (forward) or -1 (backwards): link.addEventListener("mouseleave", () => gsap.to(tl, { // Conditionally set the timescale timeScale: tickerDirection ? -1 : 1, overwrite: true })); That seems to work the way you intend. Hopefully this helps. Happy Tweening!
  23. thanks for the additional info. I believe this has to do with overwriting. You are creating conflicting tweens and my guess is that when played in reverse the animation that animates from x:0, y:0 to x:0, y:0 is winning the battle and thus you see the box jump back to the initial start state. If you set overwrite:"auto" on tweens 2 and 3 then: tween 2 will kill the y portion of tween 1 tween 3 will kill the x portion of tween 1 I think this works for this exact scenario you have https://codepen.io/snorkltv/pen/PoLZZaE?editors=0010 If you set overwrite:true then tween 3 will kill BOTH tween 1 and tween 2. You can give it a try to see how that looks (bad) from the docs https://gsap.com/docs/v3/GSAP/Tween I also think this video will help with overwrite modes I know you are saying that the tweens are automatically generated, but my advice would be to add whatever logic necessary to avoid creating conflicting tweens in the first place. Hopefully overwrite:auto solves the problem Carl
  24. Hi, Thank you again for clarifying on the issues in the implementation of logic. The solution works well in the CodePen, but I observed a strange issue with Pinned container in my actual project. Unfortunately this does not happen in CodePen and I don't know how can I explain it well enough for you to understand. But let me try. Whenever I am scrolling with container pinned, and like 1 or 2 of the 4 cards have already animated (to the top), if I refresh the page at that very point, and when the page reloads, it doesn't seem to start from that particular point - say for example, when container gets pinned and all the cards animate to the top with stagger, and then I scroll further to animate first card to the top, then I refresh the page, it logs card's index "0 enter" but the card will be still there. And when I scroll further, the next card starts animating with the first card still there. To investigate on this issue I commented out all the code keeping only the pinned trigger, initial set method for cards and the main ScrollTrigger for staggered animation of cards like below mentioned code. To my surprise, this didn't work as well - it works fine when I have not scrolled to the pinned section and reload, but when I try to reload the page after scrolling into the pinned section the trigger markers jump to some unimaginable point(usually at the very top of the page), not at all at the same point before reload. And the cards animation stops there because the it will never hit the trigger. Upon carefully observing this, I saw a strange jumping behavior of scrollbar thumb whenever page reloads, it goes up once and then come to the original position or vice versa sometimes. In CodePen it always stayed at the top on refresh, although I don't have any specific code to scroll to the top on reload, but this is how it is. To check it further, I commented-out the pinned container ScrollTrigger and the reloaded the page - to my surprise, this time the scroll thumb was not jumping up and down at all, it stayed there no matter whatever the scroll position is. So, pinning the container is causing the issue here. I hope I made myself clear about the issue I am facing here, unfortunately I am not able to show this very awkward behavior with pinned sections. And I do not have any clue on how do I resolve this. Please help. ScrollTrigger.create({ id: "pinned", trigger: '.pinned', start: 'top top', pin: true, end: "+=" + pinDistance, }); gsap.set(cards, { y: 1000, rotate: (index) => -angle * index, zIndex: (index) => cards.length - index, }); ScrollTrigger.create({ trigger: '.cards_wrap', start: 'top top', end: 'bottom bottom', // markers: true, onEnter() { console.log("enter"); gsap.to('.card', { y: 0, duration: 1, delay: 0.5, stagger: { amount: 0.5 }, overwrite: true, }); }, onLeaveBack() { console.log("leave back"); gsap.to('.card', { y: 1000, duration: 1, stagger: { amount: 0.5, }, overwrite: true }); } });
  25. I noticed several problems: It's extremely inefficient code, performance-wise. Every single time the user scrolls at all, you're creating new tweens on the same elements, over and over and over again, without even overwriting (so they'd be fighting for control). At the very least, I'd recommend setting overwrite: "auto" or overwrite: true, but honestly I think I'd re-engineer the entire approach so you're not constantly doing that on every scroll. It's much more efficient to animate to scale: 0.9 or scale: 1 instead of transform: "scale(0.9)" or transform: "scale(1)". And instead of constantly creating new tweens, if your goal is to just go back and forth between those two values, why not just create a single tween instance and then play() or reverse() it? You created a "timeline" variable and you only put a set() call in it, so it literally has no duration at all. Well, it's 0 seconds. And then you're restarting that but I assume you're asking us how you could smoothly go back to that initial value, right? If so, just create a tween that goes to that value. You're not doing any cleanup of your Lenis stuff. Beware that React calls hooks TWICE in strict mode. So be careful - you might accidentally be creating redundant Lenis stuff. That's not a GreenSock product, so we can't really support it here. It's very difficult to offer advice when we don't have a minimal demo. If you'd like more help, please make sure you create a minimal demo that clearly illustrates the issue. That'll GREATLY increase your chances of getting a good answer. Here's a starter template with Next.js that you can fork: https://stackblitz.com/edit/nextjs-5cm4qn
×
×
  • Create New...