RolandSoos

BusinessGreen
  • Content Count

    131
  • Joined

  • Last visited

Community Reputation

33 Newbie

About RolandSoos

  • Rank
    Advanced Member

Recent Profile Visitors

1,900 profile views
  1. RolandSoos

    Missing frames after addPause callback resumed

    As I know the cause of the problem, I was able to find solution for this issue, but I think it still worth some of your time to see if GSAP should be improved with the previous idea. First of all I was able to find a more easier method to simulate the problem. Just limit the fps while the playhead reaches the addPause tween. TweenMax.ticker.useRAF(false); TweenMax.ticker.fps(2); Then in the addPause callback remove the fps limitation tl.addPause(..., function(){ TweenMax.ticker.useRAF(true); TweenMax.ticker.fps(undefined); }); The solution get the tween's exact position on the timeline and in the addPause callback, seek to that position. It will result that the playhead will be in the right position. Downside: I have to store every addPause's position after adding them, to be able to know where to seek on the timeline. var pauseTime2; tl2.addPause("+=0.001", function() { this.seek(pauseTime2); button1.promise.then(function() { tl2.play(null, false); }); }); pauseTime2 = tl2.recent().startTime(); When the solution fails If in the addPause callback we instantly play the timeline, then seeking back -> GSAP will not honor the timings, which is bad and I see why the current implementation of GSAP is good. var pauseTime2; tl2.addPause("+=0.001", function() { this.seek(pauseTime2); //button1.promise.then(function() { tl2.play(null, false); //}); }); pauseTime2 = tl2.recent().startTime(); So to properly manage this situation, the seek should be conditional. If the promise already resolved when the addPause callback happens, we should continue to play and GSAP will honor timing. If it is not resolved then we do not need to honor timing, so we can simply seek back and play when the promise resolved. var pauseTime2; tl2.addPause("+=10.001", function() { if (!button1.promise.resolved) { this.seek(pauseTime2); button1.promise.then(function() { tl2.play(null, false); }); } else { tl2.play(null, false); } }); pauseTime2 = tl2.recent().startTime(); So, I can manage this situation on my own. Final pen where red box is the bad result and green is the right result How could GSAP manage this situation? Two possible solution... #1 solution addPause callback could have an optional boolean return value. This return value could indicate if we need this pause or not. tl2.addPause("+=10.001", function() { if (!button1.promise.resolved) { button1.promise.then(function() { tl2.play(null, false); }); // tell GSAP that it is a real pause, so pause the timeline, so // GSAP could adjust the position of the timeline to the exact position of the pause. return true; } // we do not need the pause, so the timeline can continue to play. return false; }); #2 solution If the pause reason of the timeline is addPause, then GSAP should check on the next tick if there way a play call on this timeline. If there was a play call on the timeline until the next tick, GSAP should continue to play without affecting the timings. If there was not any play call on the timeline until the next tick, GSAP can adjust the playhead position back to the addPause position as there is no honorable timing anymore for this pause. In my opinion #2 solution is best to solve race condition issues and works best with promises. tl2.addPause("+=10.001", function() { var ret = false; button1.promise.then(function() { ret = true; tl2.play(null, false); }); // return will be always as the promise.then is async and will set ret to true later return ret; });
  2. RolandSoos

    Missing frames after addPause callback resumed

    Thanks Jack! I was able to create a Codepen as I know the reason: https://codepen.io/anon/pen/qLJevO?editors=0010 Open up the console Watch for the error line: _pauseTime set 0.161 Run the pen again until you do not get bigger _pauseTime than ~0.04. Best if bigger than 0.1 Click on Resolve #1 and watch that the animation will jump several frames. Well if I have the following timeline var tl = new TimelineLite(); tl.addPause("+=0.001", function() { button1.promise.then(function() { tl.play(null, false); }); }); tl.to("div", 2, { x: 1000 }); I see your point about lag smoothing and that would be a great reason, but: I assume that the timeline gets paused at that position and when I resolve the promise, the play will continue from that position. I think it makes no sense to honor timings in an addPause case as the timeline is paused. So addPause should be an exception. If there was a lag between the timeline creation and addPause's pause(), I do not think that lag should affect the future when the timeline plays again. There is a reason why you paused the timeline at that given point of time addPause is already an exception in GSAP, try the following pen: https://codepen.io/anon/pen/ebQOVN?editors=1010 If GSAP would honor the timing, then the second red box should jump near to the black line instantly as it has the same position as the addPause. But the addPause was an exception and it does not allow future tweens to render on that timeline in the very same tick.
  3. RolandSoos

    Missing frames after addPause callback resumed

    Possible solution could be: p.render = function (time, suppressEvents, force) { ........ ........ while (tween) { next = tween._next; //record it here because the value could change after rendering... if (curTime !== this._time || (this._paused && !prevPaused)) { //in case a tween pauses or seeks the timeline when rendering, like inside of an onUpdate/onComplete break; } else if (tween._active || (tween._startTime <= curTime && !tween._paused && !tween._gc)) { if (pauseTween === tween) { this.pause(); // If we pause the animation by a pauseTween, then set the proper _pauseTime to prevent frame skipping this._pauseTime = this._startTime + pauseTween._startTime; } if (!tween._reversed) { tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); } else { tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); } } tween = next; } The pause() call sets the this._pauseTime, which might be not good. So if there is a pauseTween, then set the value of this._pauseTime to the sum of the _startTime of the timeline and the _startTime of the pauseTween. this._pauseTime = this._startTime + pauseTween._startTime;
  4. RolandSoos

    Missing frames after addPause callback resumed

    More details while debugging: When the timeline created, GSAP sets its _startTime: p.add = p.insert = function (child, position, align, stagger) { var prevTween, st; /*if(window.aaaa) { console.log((new Date()).getTime(), 'add to timeline', child._totalDuration); }*/ child._startTime = Number(position || 0) + child._delay; At 60ps, there is a frame in every 0.01666s Example: new TimelineLite() _startTime becomes 0.236 | timeline.rawTime() -> 0 | timeline._timeline._time -> 0.236 here the timeline created and starts to play as there is an addPause at 0.001, the timeline will pause: _pauseTime becomes 0.318 | timeline.rawTime() -> 0.089 | timeline._timeline._time -> 0.318 Exception: In an ideal world _pauseTime should be _startTime + 0.001 as we stopped there => 0.236+0.001=0.237 In real, it took 0.089s which is ~5 frame if we would have the 60fps at page load. Conclusion: When the fps is constant, it does not matter if the _pauseTime is perfectly aligned with addPause position or not as in the worst case we lose only 1 frame by this which you are unable to notice. The problems comes when you have low fps at the start of the timeline and high fps when the paused time resumed, I think this is the scenario with my complex page. There is less than 60fps during new TimelineLite(), addPause(0.001, ...) (I think we can calculate the fps as it took 5 "60fps frames" to reach the addPause, so 60/5 = 12fps). Then when the play happens, GSAP thinks that those 5 frames are already rendered and they will be skipped.
  5. Well, first of all I'm sorry that every day I have a problem. For the current one, I was not able to create a Codepen which produces this issue. In my real world example, I have an addPause at position 0.001, so when the timeline reach this position, it will wait until the click promise resolved: https://codepen.io/anon/pen/QzZZpo?editors=0110 Also in my scenario, I have a complex scene with lot of animated elements ~100 which repeated all the time. When the event happen which resolves the promise of the addPause and plays the timeline, it seems like that the animation is skipping several frames. The second, third etc. time when it reaches the addPause and the play promise resolved again, the animation is running smoothly. I tried to reproduce the very same issue at CodePen, but it only happened when I animated 10000 other small boxes, so that might be unrelated. Screen recording (watch in full hd) 0:05 I click on the zeppelin. The Zeppelin's promise resolved, so it starts to play from the addPause. This part is important as this is where you will see that the zeppelin jumps instantly in the middle of the screen. The first part of the animation is missing. Console contains the following timings: 0:19 in resolved, this is when the zeppelin was clicked, after this line the timings when this event happened: current js time: 1547202313240 .time() of the zeppelin's timeline: 2.4220000000000006 The next timings are from the onUpdate event of the zeppelin's timeline: current js time is first and the zeppelin's timeline the second Here is a more detailed console which shows the same issue as the video: ____ JS time______ .time() .progress() Play 1547203186098 0.001 0.00031201248049921997 onUpdate 1547203186121 0.2559 0.07987519500780024 onUpdate 1547203186139 0.274 0.08549141965678628 onUpdate 1547203186156 0.2919 0.09110764430577217 onUpdate 1547203186170 0.3060 0.09547581903276132 onUpdate 1547203186189 0.3239 0.10109204368174722 onUpdate 1547203186206 0.3420 0.10670826833073326 onUpdate 1547203186222 0.3559 0.11107644305772227 onUpdate 1547203186239 0.375 0.11700468018720749 onUpdate 1547203186255 0.391 0.12199687987519502 onUpdate 1547203186271 0.4070 0.12698907956318253 onUpdate 1547203186289 0.4249 0.13260530421216843 onUpdate 1547203186304 0.4399 0.13728549141965676 onUpdate 1547203186322 0.4580 0.1429017160686428 onUpdate 1547203186339 0.4750 0.1482059282371295 It seems like the first onUpdate jumped ~0.252s which I think could result that huge jump as the original animation's duration is 0.8s. The timelines whole duration 3.205 If I change the addPause position from 0.001 to 1s the animation plays nicely without jump and this is the console output. There is only ~0.04s jump between the first two. ____ JS time______ .time() .progress() play____ 1547203837755 1 0.23786869647954326 onUpdate 1547203837780 1.041 0.24762131303520452 onUpdate 1547203837798 1.058 0.25166508087535683 onUpdate 1547203837813 1.074 0.2557088487155089 onUpdate 1547203837829 1.090 0.25951474785918166 onUpdate 1547203837846 1.108 0.263558515699334 onUpdate 1547203837862 1.124 0.26736441484300666 onUpdate 1547203837881 1.143 0.271883920076118 I tried several position values and it seems like it has this frame skipping behavior, when the position is smaller than 0.2s. Above 0.2s I get 0.04s between the first two. Under 0.2s, the delay is increasing with lower values. I tried to place addPause to 1.001s and seek the timeline to 1s, but this does not solved the issue. Ps.: if I won't be able to solve this, I will remove the addPause from the start of the timeline and I will start the timeline initially paused and will start to play when the event happens.
  6. Thanks, if it does not improve performance, then I will stay with multiple nested timelines. (I had a misunderstanding with immediateRender and I thought the nested timelines cause the issue. I thought if I have multiple fromTo tweens on the same element and same properties in the same timeline, then I do not have to take care setting immediateRender to false when required. Well, it turned out I have to take care of it )
  7. For example I have 2 timelines and both contains several tweens. These two timelines are placed to a main timeline both of them to position 0. If I would like to remove the two additional timelines, what would be the best way to place the tweens to the timeline. (Try to think in general with a lot of involved tweens.) Getting the .recent() tween from the timeline and sum .startTime() and .totalDuration() of the tween is the right way to get the upcoming position of that tween?
  8. RolandSoos

    addPause: the behavior is unexpected

    I had a little time for debugging. I used the current version of uncompressed tweenmax.js. Stacktrace First column is the first console log Second column is the last console log right before the callback fired twice. Third column is the console log when the event fired twice It first and the third call is fine and the exception is the second call stack. It seems like the double firing happens when the ticker is not active an GSAP wakes it up. tl.pause(); in the last callback sometimes sends the ticker to sleep Animation._updateRoot = TweenLite.render = function() { var i, a, p; if (_lazyTweens.length) { //if code is run outside of the requestAnimationFrame loop, there may be tweens queued AFTER the engine refreshed, so we need to ensure any pending renders occur before we refresh again. _lazyRender(); } _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false); _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false); if (_lazyTweens.length) { _lazyRender(); } console.log(_ticker.frame , _nextGCFrame); if (_ticker.frame >= _nextGCFrame) { //dump garbage every 120 frames or whatever the user sets TweenLite.autoSleep to _nextGCFrame = _ticker.frame + (parseInt(TweenLite.autoSleep, 10) || 120); for (p in _tweenLookup) { a = _tweenLookup[p].tweens; i = a.length; while (--i > -1) { if (a[i]._gc) { a.splice(i, 1); } } if (a.length === 0) { delete _tweenLookup[p]; } } //if there are no more tweens in the root timelines, or if they're all paused, make the _timer sleep to reduce load on the CPU slightly p = _rootTimeline._first; if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) { while (p && p._paused) { p = p._next; } if (!p) { _ticker.sleep(); } } } }; Which outputs: So I think it is related with the garbage collection I think. Another interesting details, that the .play() suppressEvents is default to true, so there should not be any events, but when this wake exception happens at garbage collection, the _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false); gets called which does not suppress the events. I hope it helps
  9. RolandSoos

    addPause: the behavior is unexpected

    Thanks @Dipscom! My current system use tiny timelines and manages their statuses and such. There is no real problem with it, but with a single timeline, I would be able to achieve better result in the following areas. Single timeline would allow to use the same timeline as linear or event based mode. I would be able to simply change progress based on scroll for example. when the canvas in my system changes, I could simply invalidate the timeline without errors. Currently I have to invalidate all timelines in the right order as their happen on the same element/props also I must restart the animations from the start point. With a single timeline I could invalidate and just "resume" from the current point. not care for immediateRender. As the current system animates the same element's prop in different timelines, I must use the proper tween and it results in lot of testing. use timeScale easily. throw out a lot of code related to timeline managing. Probably I would be able to solve all of these things with tiny timelines and a custom timeline manager implementation. But, why would I write a new timeline controller if GSAP's might be able to solve this issue, right? BTW: I near to the finish with my single timeline and it looks and works great. I haven't seen any bug related to this double firing, probably because I still manage statuses and my callbacks verify if the actual status is the right one. So it is not so important for me, it would be just nice to know if this bug could result any other anomalies.
  10. RolandSoos

    Chrome -> flickering on scale

    Thanks Jack, I will try the fix tomorrow. It seems fine on Macbook Air's Chrome. I saw the problem on Win 10 with AMD graphics. The first box and text has a vibration during the animation.
  11. RolandSoos

    Chrome -> flickering on scale

    I have 3 boxes animated with scale on this Codepen. #1 has a parent with overflow hidden -> the scale flicking during the scale animation #2 has a parent with overflow hidden and the parent has a parent with perspective -> the scale animation is fine #3 has a parent with overflow hidden and perspective -> the scale animation is fine Is adding perspective to the overflow:hidden element is the right solution or is there a better way to fix this? Tested with Chrome 71. Firefox seems fine.
  12. RolandSoos

    addPause: the behavior is unexpected

    I removed every loaded JS files except TweenMax.min.js, also set the postition offset to +=0.01 and reduces/improved the test. It will die with error message on the console when the callback fired twice. https://codepen.io/mm00/pen/REMGre?editors=0011
  13. RolandSoos

    addPause: the behavior is unexpected

    Thanks @Jonathan, it happened once in debug mode too
  14. RolandSoos

    addPause: the behavior is unexpected

    Well, I can reproduce it in every 3-5 Codepen run, it happens after 5-7 iteration. Also The window was focused all the time, so I do not think it is relevant. In my old system I have 3 simple timelines and they contain tweens for the very same element. The system manages to play, pause, seek etc... all of them. The timelines play only when their dependency met. Here is an example (events are optional in the system) TL #1 Plays when user clicked a button and if this is the first time when TL #1 plays TL #2 Plays when TL #1 completed and this timeline repeats itself until the user clicks another button. If the button clicked TL #2 plays until onComplete TL #3 Plays when TL #2 completed When I add them to a single timeline, it gets a little complex, but I think it is possible with GSAP. This is why I experience with these edge cases and examples addLabel -> 'TL1' -> Position 0 addCallback -> Position +=0.001 -> Pause the timeline if TL #1 dependency not met (Also play the timeline in the future when dependency met). Position offset added so when I seek to TL1 label in the future I do not need suppressEvents:false add TL1 tweens addCallback -> Notify system that TL1 completed addLabel -> 'INITIAL' -> Position +=0.001 addLabel -> 'TL2' -> Position +=0.001 addCallback -> Position +=0.001 -> Pause the timeline if TL #2 dependency not met (Also play the timeline in the future when dependency met). addLabel -> 'LOOP' -> Position +=0.001 add TL2 tweens addCallback -> Position +=0.001 -> if the dependency not met seek back to loop label all the time. .seek('LOOP') addCallback -> Position +=0.001 -> Notify system that TL2 completed addLabel -> 'TL3' -> Position +=0.001 addCallback -> Position +=0.001 -> Pause the timeline if TL #3 dependency not met (Also play the timeline in the future when dependency met). add TL3 tweens addCallback -> Position +=0.001 -> Notify system that TL3 completed. Based on the options, might .seek('TL1') Public actions allowed on this timeline are: play pause seek('TL1') seek('INITIAL')
  15. RolandSoos

    addPause: the behavior is unexpected

    Thanks @Dipscom! I started to work with your suggestion. Well, if I assume 60fps 1/60=~0.01666, the offset should be smaller than that number. So the suggested 0.01 should be fine, but I tried it with 0.0001. I'm not sure if the following error related to float rounding or something wrong with my code. I might need to add multiple labels/callback at the "same" offset position, so I would like to go 0.001 as that would give 16 extra spaces for a tick at 60fps. https://codepen.io/anon/pen/vvdqEm Normal console output would be: Start pause at IN pause at Complete pause at IN pause at Complete pause at IN pause at Complete pause at IN pause at Complete But sometimes I get the following: If it is really a float rounding issue, is there any chance that it happens when the small offset is 0.01 or 0.001?