Jump to content
Search Community

avancamp

Members
  • Posts

    42
  • Joined

  • Last visited

Everything posted by avancamp

  1. Thanks @GreenSock -- any chance I could get this in npm tarball form to verify the fix in my real-world app?
  2. Hi again, here's another repro with the release build of 3.2.0. I didn't make a codepen because the starter codepen is still using 3.1.1 it seems: Things to note: If the .call is removed, then the .to plays as expected. If autoRemoveChildren is set to false, the .call is fired but the .to doesn't play. Again, commenting out the .call makes the .to play as expected. import { gsap } from './esm/index.js'; const header = document.createElement('h1'); header.textContent = 'Hello world.'; document.body.appendChild(header); let called = false; // On a timer because I'm not sure that putting this in // an onComplete does the right thing in this test case. setTimeout(report, 1000); const tl = gsap.timeline({autoRemoveChildren: true}); // Everything in here is skipped. setTimeout(() => { tl.call(() => { called = true; }); tl.to(header, { duration: 0.2, y: 100 }); }, 500); // Seems to start failing around 300ms. function report() { if (called) { console.log('Success!'); } else { console.log('Failed, callback skipped.'); } }
  3. I think I'm all set now. I just ran my test suite 10 times and had zero failures. Thank you very much for accommodating my niche use case and being so responsive.
  4. The new build does fix that case, but seems to have regressed in another area and caused this case to fail: import { gsap } from './package/index.js'; const header = document.createElement('h1'); header.textContent = 'Hello world.'; document.body.appendChild(header); let called = false; // On a timer because I'm not sure that putting this in // an onComplete does the right thing in this test case. setTimeout(report, 1000); const tl = gsap.timeline(); // Everything in here is skipped. setTimeout(() => { tl.to(header, { duration: 0.2, x: 100 }); tl.call(() => { called = true; }); }, 100); function report() { if (called) { console.log('Success!'); } else { console.log('Failed, callback skipped.'); } }
  5. One more edge case, I think this may be the last one: import { gsap } from './package/index.js'; const header = document.createElement('h1'); header.textContent = 'Hello world.'; document.body.appendChild(header); let called = false; // On a timer because I'm not sure that putting this in // an onComplete does the right thing in this test case. setTimeout(report, 1000); const parent = gsap.timeline({ autoRemoveChildren: true }); parent.add(makeChildA()); parent.progress(1); setTimeout(() => { parent.add(makeChildB()); parent.progress(1); }, 100); function makeChildA() { const tl = gsap.timeline(); tl.to(header, 0.2, { x: 0, y: 150 }); return tl; } function makeChildB() { const tl = gsap.timeline(); tl.to(header, 0.2, { x: 150, y: 0 }); tl.call(() => { called = true; }); return tl; } function report() { if (called) { console.log('Success!'); } else { console.log('Failed, callback skipped.'); } }
  6. I think it might be fixed! I can't get the issue to repro on my real-world app anymore. I'll keep banging on it and report back if I manage to find any more issues.
  7. Okay, here's an actual repro that makes sense and won't waste your time lol: import { gsap } from './package/index.js'; const header = document.createElement('h1'); header.textContent = 'Hello world.'; document.body.appendChild(header); let called = false; // On a timer because I'm not sure that putting this in // an onComplete does the right thing in this test case. setTimeout(report, 1000); const parent = gsap.timeline(); parent.add(makeChild()); // Yes, this being delayed is part of the repro. requestAnimationFrame(() => { parent.progress(1); }); function makeChild() { const tl = gsap.timeline(); tl.call(async () => { tl.pause(); await waitForImageToLoad(); tl.resume(); }); console.log('Creating grandchild timeline and adding it to the child timeline...'); // This entire grandchild timeline is skipped. tl.add(makeGrandChild()); return tl; } function makeGrandChild() { const tl = gsap.timeline(); tl.to(header, { duration: 0.2, rotation: 15, x: 100, y: 150 }); tl.call(() => { called = true; }); return tl; } function waitForImageToLoad() { return new Promise(resolve => { setTimeout(() => { resolve(); }, 300); }); } function report() { if (called) { console.log('Success!'); } else { console.log('Failed, callback skipped.'); } }
  8. Haha, it comes from having used GSAP for about 6 years and having written many tens of thousands of lines of animation code with it. At this point I have a massive library of really complex graphics I can fire up which, for better or worse, strain parts of GSAP in ways they are perhaps not intended to be strained. I have unfortunately found another repro, this one doesn't necessarily involve progress or timeScale either: EDIT: Gosh I'm a fool, this repro makes no sense and will always fail lmao. Sorry, let me go try again...
  9. The issue has changed, but I don't think it is fixed. Here's another repro which adds autoRemoveChildren and re-orders the tweens/calls, and gets the issue to happen again. Of note is that if the final tween is commented out, the test passes. Very strange: import { gsap } from './package/index.js'; const header = document.createElement('h1'); header.textContent = 'Hello world.'; document.body.appendChild(header); let called = false; const timeline = gsap.timeline({ autoRemoveChildren: true, onComplete() { report(); } }); timeline.call(async () => { timeline.pause(); await waitForImageToLoad(); timeline.resume(); }); timeline.call(() => { called = true; }); timeline.to(header, { x: 300, duration: 1 }); // When this tween is commented out, the test passes. timeline.to(header, { y: 100, duration: 1 }); timeline.progress(1); function waitForImageToLoad() { return new Promise(resolve => { setTimeout(() => { resolve(); }, 300); }); } function report() { if (called) { console.log('Success!'); } else { console.log('Failed, callback skipped.'); } }
  10. @GreenSock Thanks, I'll give it a whirl tomorrow morning and report back. Also, if this edge case is just too strange to accommodate, I would accept something that prints a warning to the console advising against this behavior if GSAP detects this sort of thing. I don't mind changing my code, as long as I can't accidentally walk into this footgun again. And yeah, broadcast graphics are super weird and require all kinds of weird uses of tooling haha. There's so many things I do that must seem absurd when viewed from a normal web dev perspective. For example, I have graphics that ideally need to run for 7 full days straight. That's a very long-lived timeline, which is constantly reacting to events and queuing up animations. I also need to have systems which can immediately advance any animation to its end state, so that an operator can preview what the graphic will look like before they air it live. Stuff like that.
  11. Okay, that latest beta fixed all the new issues, thanks for that. I am now back on my original issue: .call being skipped. I believe I have found a minimal repro. The problem is indeed caused by pause/resume. I believe the issue is that when progress(1) and/or timeScale(99) are used, increasingly large parts of the timeline are considered to be "under the playhead" in a given tick, and therefore more and more things get skipped when resume is invoked, due to it always setting suppressEvents = true. This is a 3.1.1 CodePen, but the issue appears to be the same on 3.2.0-beta. Look at the console output to see if the test passed or failed: EDIT: Also, if autoRemoveChildren: true is added to the timeline, it appears that onComplete gets called twice? https://codepen.io/Lange/pen/xxGwOqY?editors=0010
  12. It may be rare in other codebases, but it is used heavily in mine haha. For whatever reason, it's a pattern I've found myself using extensively. Thanks for updating the build, will report back once I test it. EDIT: Well, not "for whatever reason", the reason is that GSAP timelines are a great tool for creating long-running sequential queues of things, so I often have these timelines that live for the lifespan of the page which get things added to them when various events fire.
  13. Got it, here's an even more minimal repro. The issue seems to occur when a timeline is created, but doesn't have any children added to it for a while. import { gsap } from './package/index.js'; const header = document.createElement('h1'); header.textContent = 'Hello world.'; document.body.appendChild(header); const timeline = gsap.timeline(); console.log('parent immediately after construction', timeline.parent); // This delayed anim never plays. setTimeout(() => { timeline.to(header, { x: 300, duration: 1 }); console.log('parent 1.5s later', timeline.parent); }, 1500);
  14. Very interesting -- this new build fixes this bug for that minimal repro, but not for my real-world app, so there is some other condition under which this same bug (timeline not playing, parent being null unexpectedly) may happen. I'll keep trying to find another repro.
  15. I was indeed not on that exact build, but this new build seems strange in that the .parent property is being unset at unexpected times? Here's the minimal repro: import { gsap } from '../node_modules/gsap/index.js'; const header = document.createElement('h1'); header.textContent = 'Hello world.'; document.body.appendChild(header); class Foo { constructor() { this.timeline = gsap.timeline(); console.log('parent immediately after construction', this.timeline.parent); } right() { this.timeline.to(header, { x: 300, duration: 1 }); return this.timeline; } left() { this.timeline.to(header, { x: 0, duration: 1 }); return this.timeline; } } const instance = new Foo(); // This call works. instance.right(); // This delayed call does not. // Of note is that the timeline no longer has a parent. setTimeout(() => { const anim = instance.left(); console.log('parent 1.5s later', anim.parent); }, 1500); I can't reproduce this in a CodePen using https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/gsap-latest-beta.min.js, maybe it's not the same build as that zip you linked.
  16. It is possible that I am somehow using an incorrect build of 3.1.2 beta. If you could send an npm-formatted zip of the latest build, I can try that in my real-world app and see if its behavior changes.
  17. I have tested 3.1.1 and the 3.1.2 beta. Here is a 3.1.1 codepen that reproduces this issue every time for me on Chrome and Firefox on two different computers: https://codepen.io/Lange/pen/jOPPLoX?editors=0010
  18. Potentially related, this code will return NaN, which I think ends up propagating through several parts of GSAP and causing potentially undefined behavior: const tl = gsap.timeline(); console.log(tl.totalProgress());
  19. Adding a clamp might help, but I worry that there's something deeper that I'm not understanding. Particularly: why does this CodePen behave differently under different browser conditions? Sometimes progress grows unbounded, sometimes it doesn't.
  20. I am potentially close to finding a repro. First question: is it intentional that gsap.globalTimeline.progress() can sometimes grow unbounded as demonstrated in this CodePen? (check the console output) EDIT: On first run, the issue doesn't seem to happen? Try hitting Run again and it seems to happen then. EDIT 2: The issue is only present if the JS Console is open when Run is pressed... I do not understand. EDIT 3: This issue is... hardware dependent somehow? It doesn't happen on my laptop, but does on my desktop. I am so confused. EDIT 4: It has happened on my laptop exactly once after a few dozen tries. It happens on my desktop every time consistently. https://codepen.io/Lange/pen/WNvvRap?editors=0010
  21. I was unable to resolve this issue. I resorted to instead spamming `gsap.globalTimeline.progress(1)` on an interval. Something specifically about raising `.timeScale` just makes my timelines misbehave.
  22. @ZachSaucier Absolutely, which is why I want to get this test suite to be fast using whatever hacks are available to me right now, so that I can rewrite my animations to be more idiomatic without wanting to pull my hair out while waiting for a super slow test suite to run. Faster tests means I can get my code to a more idiomatic implementation much more rapidly. In short: I know how bad my code is right now, and I'm seeking help implementing this strange timeScale hack so that I can be more efficient in making my code less bad. It is a bandaid solution that will help me get to a real solution.
  23. @ZachSaucier While I agree that is ideal, it is much harder in this framework than it should be because of mistakes I made 5 years ago. Specifically, there is no single entrypoint into most of these animations. These timelines will fork off other independent timelines from various .call handlers, and the event callbacks of these timelines (onStart, onComplete, etc) will pause and resume each other. It is a very dumb and very complex web and there is no single thread to pull on. The simplest thing is just to bang on the global timeline, for now. Though, in general, I think manipulating the global timeline for the purposes of screenshot testing is pretty ideal, and once these animations are better-written I'll still probably use gsap.globalTimeline.progress(1) because it'll just work everywhere (in theory).
  24. Alright, here is a pretty long and in-depth explanation of what I'm trying to do and why. Apologies for the wall of text: Background: I make graphics for online video broadcasts using GSAP. I do not make traditional websites, and do not have the same use cases, goals, concerns, or limitations as an actual web dev. Everything I make is in the context of live video broadcast graphics systems. The specific codebase in question is 5 years old, under active development, and used regularly in production on major broadcasts. It is stable and, in normal use, exhibits no GSAP-related issues. However, many of our animations are extremely complicated, and large refactors of them are an absolute last resort. Goal: Make all animations on the page finish instantly (or rather, as close to instant as possible), regardless of when they are added, for the purposes of automated screenshot comparison testing. About the screenshot test system: The test framework loads one of my graphics pages in Puppeteer. It then issues a command to the page to play some animation. It then waits for a hardcoded amount of time for the animations to finish and takes a screenshot of the page, which it compares against a known-good reference. If there is a discrepancy, the test fails. This system currently works, but is very slow because it just has to wait for all animations to play out in real-time before it can take the screenshot of their end state (the end state is the only thing we screenshot and test for). Running the test suite takes about 8 minutes, and is a major blocker to my daily productivity. This is purely my own fault for making such a flawed system, but is important context as to why I am motivated to solve this problem. Why I'm not using gsap.globalTimeline.progress(1): It is currently hard for me to know when any given page I am testing is actually done adding things to the timeline. Yes, this is a flaw in my codebase. It is something I am working to resolve, but for legacy reasons will take a long time. In the meantime, I am trying to speed up my tests using whatever means I have at my disposal. gsap.globalTimeline.timeScale(99) is the most promising short-term solution I have, because it means I can lower this hardcoded wait time substantially, and potentially run the test suite in as little as 1 minute instead of 8 minutes. Why I’m not just rewriting my graphics to use GSAP in a more idiomatic way: Long-term, that is precisely my goal. However, doing that will be profoundly difficult and slow, given how long it takes me to run my tests. If I am to pull this refactor off, I just need a hacky way of making my tests faster right now, without having to rewrite my animations first. How the issue manifests: Randomly, when running at a very high timescale, certain child timelines on the page will never reach their end state. They get "stuck" at various points. This issue has never happened in production, which only uses a timescale of 1. It only affects this primitive screenshot testing system. My timelines often do what I’ve been calling “pause manipulation”, and I’m guessing that it is at the heart of the problem. It works something like this: Start playing a "parent" animation. At some point in this animation, the parent generates an indeterminate number of children, each of which plays an animation of indeterminate length. Because it is not clear how long these child animations will be when they start, my solution in many places in this codebase is to call .pause() on the parent when these children begin running. The children run to completion, and then when they are complete I call .resume() on the parent. Yes, I know this seems like it does not make sense for many reasons, but I have to stress that this is actually rational in the context of these very complex animations and the limitations of the framework around them, and is the best compromise I could make at the time of authoring. So, in reality, I am calling .pause() and .resume() maybe 1-6 times over the span of a 5-60 second animation that consists of multiple parallel timelines which are orchestrated together. Summary: I have a large legacy codebase that works well in production. I want to have automated screenshot tests for this codebase, but I don’t want to have to wait for the animations to play out in realtime. In an attempt to fix this, I am running the animations with timeScale(99). But, when I do that, some of the timelines on the page never actually finish and get stuck at seemingly random points. When they are stuck, they are not actually paused, nor are any of their ancestor timelines (all the way up to the root) paused. And yet, they are stuck and not playing. I hope that this context is useful, and I appreciate the time and attention given towards this vague and niche issue so far.
×
×
  • Create New...