Jump to content
Search Community

RolandSoos last won the day on January 18 2019

RolandSoos had the most liked content!

RolandSoos

Members
  • Posts

    200
  • Joined

  • Last visited

  • Days Won

    1

Posts posted by RolandSoos

  1. 9 hours ago, GreenSock said:

    Your demo is behaving as I'd expect. #div2.opacity starts out as 1. You flip it to 0 in your nested timeline, and that set() records the starting value so that it can toggle it appropriately when the playhead moves forward/backward over it. So when the parent rewinds, it is correctly setting opacity back to 1. 

     

    Yeah, I think I still have confusion with immediateRender. 

    The following two examples shows where my confusion comes:

     

    #1 opacity gets the 0 value at create, but on replay it waits until the set position reached.

    nestedTl.set("#div2", { opacity: 0, immediateRender: true });

     

    See the Pen MZMKdK?editors=0010 by mm00 (@mm00) on CodePen

     

    #2 opacity gets the 0 value on the main timeline start for every replay too

    TweenLite.set("#div2", { opacity: 0 });
    nestedTl.set("#div2", { opacity: 0, immediateRender: false });

     

    See the Pen KJavXQ?editors=0010 by mm00 (@mm00) on CodePen

     

    I had the wrong conception for imediateRender. As when the immediateRender is true it renders the properties instantly. Like a simple .set() would do. So I used to spare a call with immediateRender:true. But when the timeline plays again, it does not behave like when I set the values in a different call (#2).

     

    Probably my confusion with immediateRender comes from the fact, that the immediateRender:true on from* based tweens work like I need and .set with immediateRender:true differs.

    #3 opacity gets the 0 value on the main timeline start for every replay too

    nestedTl.fromTo("#div2", 0.0001, {opacity:0}, { opacity: 0, immediateRender: true });

    See the Pen BMpdGw?editors=0010 by mm00 (@mm00) on CodePen

     

    So if I want proper result for my scenario, I have to stick with the #2 solution and that will give good result combined with invalidate() like in this topic:

    var t = timeline.time(); //store the current time in a variable
    timeline.time(0); // seek back to position 0 which allow invalidate to read the proper values
    timeline.invalidate().time(t); //set it back to that time immediately. 

     

    ---------------------------------------------------------------

    9 hours ago, GreenSock said:
    On 1/19/2019 at 11:12 PM, RolandSoos said:

    How would an example look like when a from* based tween with immediateRender: true does not render its values after the first render?

     

    Sorry, I don't really understand the question. Can you clarify a bit? I want to help - I'm just fuzzy about what you're asking ?

     

    This question was related to:

    On 1/19/2019 at 10:22 PM, GreenSock said:

    Also, to be clear, immediateRender:true does NOT mean that every time you rewind the parent timeline, those values will immediately render no matter where the child animation is placed on the timeline. immediateRender is only for literally the first render, when the animation is created. 

     

     

    Here is an example from* based example: 

    See the Pen BMpdGw?editors=0010 by mm00 (@mm00) on CodePen

    You told that immediateRender in this example does not mean that the start value will be properly rendered when the parent timeline rewinds to position 0. But I do not see how your thought affect this example's rewind. Everytime I rewind the parent, Hello World gets opacity 0 no matter what.

  2. Well, I got my first issue related this bug, so I debug deeper. Values are from the Chrome debugger.

     

    R2Tx65H.png

     

    4.624-0.001 = 4.622999999999999  // Note: Lost the precision. In a perfect world, it should be 4.623

     

    5Rz5Umy.png

     

    4.622999999999999-4.6129999999999995 = 0.009999999999999787

     

    Bc9BZB6.png

     

    0.009999999999999787-0.01 = -2.1337098754514727e-16
     

    and then GSAP end up thinking its a reverseComplete

     

    pQcParK.png

     

    I tried the following code and solved this issue:

    Line 1766

    var renderTime = time - tween._startTime;
    
    if(renderTime < _tinyNum && renderTime > -_tinyNum){
      renderTime = 0
    }
    
    
    if (!tween._reversed) {
      tween.render(renderTime * tween._timeScale, suppressEvents, force);
    } else {
      tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - (renderTime * tween._timeScale), suppressEvents, force);
    }

     

     

    In my real world scenario this bug caused trouble. I had an addPause callback at some position. To prevent another issue I mentioned in the forum earlier, my addPause callback seeks back the timeline to the real position:

    var pausePosition;
    timeline.addPause(function(){
    	timeline.seek(pausePosition);
    });
    pausePosition = timeline.recent().startTime();

     

    It pauses the timeline properly. Then in the future, when I play this timeline, because of this rounding issue the addPause callback gets fired again as rounding messed up the time and GSAP indentifies that we are in the past and calls the pause again. (It is rare, but happened several times.)

     

    Update #1:

    This float problem is not only related to my small position (0.01s) what I use in my examples. The same can happen with real world values: 

    See the Pen jdWVGL?editors=0011 by mm00 (@mm00) on CodePen

     

  3. There might be one other change needed, but I'm not sure. Checking if the value not equal with the current _paused property.

     

    Timeline

    p.paused = function (value) {
      if (arguments.length && !value && value != this._paused) { //if there's a pause directly at the spot from where we're unpausing, skip it.
        var tween = this._first,
            time = this._time;
        while (tween) {
          if (tween._startTime === time && tween.data === "isPause") {
            tween._rawPrevTime = 0; //remember, _rawPrevTime is how zero-duration tweens/callbacks sense directionality and determine whether or not to fire. If _rawPrevTime is the same as _startTime on the next render, it won't fire.
          }
          tween = tween._next;
        }
      }
      return Animation.prototype.paused.apply(this, arguments);
    };

     

  4. While debugging my code I used the timeline.paused() method without parameters to get if the timeline paused or not. I have strange results when the playhead was paused at the exact position of an addPause callback, so I checked GSAP source code and I think I found the cause.

     

    Timeline

    p.paused = function (value) {
      if (!value) { //if there's a pause directly at the spot from where we're unpausing, skip it.
        var tween = this._first,
            time = this._time;
        while (tween) {
          if (tween._startTime === time && tween.data === "isPause") {
            tween._rawPrevTime = 0; //remember, _rawPrevTime is how zero-duration tweens/callbacks sense directionality and determine whether or not to fire. If _rawPrevTime is the same as _startTime on the next render, it won't fire.
          }
          tween = tween._next;
        }
      }
      return Animation.prototype.paused.apply(this, arguments);
    };

     

    Animation

        p.paused = function (value) {
            if (!arguments.length) {
                return this._paused;
            }
            var tl = this._timeline,
                raw, elapsed;
            if (value != this._paused) if (tl) {
                if (!_tickerActive && !value) {
                    _ticker.wake();
                }
                raw = tl.rawTime();
                var raw2 = raw;
                elapsed = raw - this._pauseTime;
                if (!value && tl.smoothChildTiming) {
                    if (this._totalDuration === 3.203) {
                        //console.log('elapsed', raw, this._pauseTime);
                        console.error('new start time', this._startTime, 'Ez OK:',this._pauseTime);
                    }
                    this._startTime += elapsed;
                    this._uncache(false);
                }
                this._pauseTime = value ? raw : null;
                this._paused = value;
                this._active = this.isActive();
                if (!value && elapsed !== 0 && this._initted && this.duration()) {
                    raw = tl.smoothChildTiming ? this._totalTime : (raw - this._startTime) / this._timeScale;
                    if (this._totalDuration === 3.203) {
                        console.log('start1', this._startTime, 'ticker time', raw2, 'Raw = _totalTime', raw,);
                        window.abcd = true;
                    }
                    this.render(raw, (raw === this._totalTime), true); //in case the target's properties changed via some other tween or manual update by the user, we should force a render.
                }
            }
            if (this._gc && !value) {
                this._enabled(true, false);
            }
            return this;
        };

    When I call timeline.paused() without arguments, the value will be undefined, which evaluates as false, so it goes into the if statement. Your comment says: //if there's a pause directly at the spot from where we're unpausing, skip it.

    So based on the comment, it should run when it is called with value:false.

     

    So probably it should be:

    Timeline

    p.paused = function (value) {
      if (arguments.length && !value) { //if there's a pause directly at the spot from where we're unpausing, skip it.
        var tween = this._first,
            time = this._time;
        while (tween) {
          if (tween._startTime === time && tween.data === "isPause") {
            tween._rawPrevTime = 0; //remember, _rawPrevTime is how zero-duration tweens/callbacks sense directionality and determine whether or not to fire. If _rawPrevTime is the same as _startTime on the next render, it won't fire.
          }
          tween = tween._next;
        }
      }
      return Animation.prototype.paused.apply(this, arguments);
    };

     

     

    Ps.: Sorry, I currently do not have time to create a CodePen which illustrates the bug I had.

  5. @GreenSock

    I have a problem related to this solution. I know we talked about tweens in the original conversation, but since then I started to invalidate the whole timeline.

     

    When I have a .set() on the invalidated timeline, the original value will be lost if the timeline gets invalidated after the .set() happened. Example: 

    See the Pen gqaEze?editors=0010 by mm00 (@mm00) on CodePen

     The red box will stay visibility:hidden after the invalidate happens at second turn. 

     

    I know the logic behind this. Invalidate will force GSAP to read the values again, like it would for a .to() tween. So when the invalidate happens GSAP reads visibility: hidden and it will set that when the timeline restarts.

     

    So, as I know the cause, probably I can solve that. I just need to seek back the timeline to the starting position and then invalidate the timeline.

     

    var t = timeline.time(); //store the current time in a variable
    timeline.time(0); // seek back to position 0 which allow invalidate to read the proper values
    timeline.invalidate().time(t); //set it back to that time immediately. 

     

    Updated codepen: 

    See the Pen yZYrRg?editors=0010 by mm00 (@mm00) on CodePen

     

     

    So the question: what do you think, is it fail safe?

     

  6. Yes, it seems like the fix solved the issue. Thank you!

     

    Old:

    See the Pen bzbgpd?editors=0011 by mm00 (@mm00) on CodePen

     

    New:

    See the Pen omvBJR?editors=0011 by mm00 (@mm00) on CodePen

     

     

     

    I'm not sure if you are interested in another hard to noticeable bug to fix in addPause :)

     

    There might happen that a condition already met when I reach addPause. First though, simply tl.play() in the addPause callback and I will be fine. But, as the addPause happens earlier than tweens and the pause cancels the rendering of that frame. It results if you instantly resume the tween, you will notice a skipped frame if you watch closely. On my example watch the middle of the animation. Hard to notice but you will see the result of the one missing frame:

    https://codepen.io/mm00/pen/xMKgMm?editors=0011

     

     

    I know, I could use removePause, but that would take too much effort as I would need to place that back on the onComplete event of the timeline also I need that callback. :)

     

    As a temporary solution I get the tween created with addPause and I play with the data property:

    // If I allow pause
    tween['data'] = 'isPause';
    
    // If I want to skip the pause
    tween['data'] = '';

     

     

     

  7. See the Pen MZMKdK?editors=0010 by mm00 (@mm00) on CodePen

     

    This is the example what I needed to solve with a very short duration fromTo tween instead of the .set(). I'm not sure how it would be possible to solve if the parent timeline is not accessible for the code which creates the nested timeline.

     

     

    Also I'm curious about the exception what you described. How would an example look like when a from* based tween with immediateRender: true does not render its values after the first render?

  8. You had a logic issue. Your original code animated the changeColors object to the very same values, so nothing happened:

    var colors =  {top:"#feaf70", bottom:"#feaf70"},
        changeColors = {
            color0: '#a2e0f8',
            color1: '#0faeec'
        };
    	
    	
    tl.to(changeColors, 0.3, {
    	colorProps: {
    		color0: changeColors.color0,  // #a2e0f8
    		color1: changeColors.color1   // #0faeec
    	},
    	ease: Power1.easeOut,
    	onUpdate: applyProps
    })
    
    tl.to(changeColors, 0.3, {
    	colorProps: {
    		color0: colors.top,  // #feaf70
    		color1: colors.bottom  // #feaf70
    	},
    	ease: Linear.easeNone,
    	onUpdate: applyProps,
    	delay: -.3
    })

     

     

    Solution

    • changeProps should hold the initial values
    • Then you tween the changeColors to the values of originalColors
    • Then you can then you can tween back to the initial values
    var colors =  {top:"#feaf70", bottom:"#fc669e"},
        changeColors = {
            color0: colors.top,
            color1: colors.bottom
        };
    
    //....
    
    tl.to(changeColors, 0.3, {
      colorProps: {
        color0: '#a2e0f8',
        color1: '#0faeec'
      },
      ease: Power1.easeOut,
      onUpdate: applyProps,
      delay: -.3
    })
    
    //...
    
    tl.to(changeColors, 0.3, {
      colorProps: {
        color0: colors.top,
        color1: colors.bottom
      },
      ease: Linear.easeNone,
      onUpdate: applyProps,
      delay: -.3
    });

     

     

    See the Pen aPgLVE?editors=0011 by mm00 (@mm00) on CodePen

     

    • Like 3
    • Thanks 1
  9. Well, your explanation sound right.

     

    My misunderstanding comes from that I assumed setting immediateRender:true on a set based tween gives the same result as the fromTo based tween. But in real, the result is not the same.

     

    Real use case:

    I have several nested timelines and I can not place my .set() to the top of parent's timelines 0 position, so the set has a non zero position on the timeline. I wanted the use the advantage of fromTo's immediateRender:true, by knowing even if I place an immediateRender:true tween later on the timeline, I can be sure when the parent timeline starts to play again, my immediateRender:true tweens will sets their properties instantly. Here you check it: https://codepen.io/mm00/pen/MZMKdK?editors=0010

     

     

    7 hours ago, GreenSock said:

    x starts at 0, then you put a set() call 3 seconds into the timeline to make it jump to 100 but since immediateRender is true, it also renders that immediately (at the time your set() call is evaluated). But that doesn't change the fact that the actual tween/set is positioned at the 3-second spot. So later, when you jump back to a time of 0, that's BEFORE the 3-second spot, thus the set() reverts to 0 as it should. Am I missing something? 

     

    Check this pen. It has nested timelines one for set tweens and one for fromTo tweens. At second run the set based timeline will differ from the fromTo based timeline.

    See the Pen bOPEvO?editors=1010 by mm00 (@mm00) on CodePen

     

  10. @GreenSock I just learned an interesting fact related to this topic. If I set immediateRender:true on a .set() tween it works like a fromTo tween would work. The tween position on the timeline does not matter and GSAP renders the property instantly. I think this is right. The problem happens when I play again this timeline, the box will stay at x:0 instead of the x:100 from the immediateRender:true

     

    In the following example, you can see that

    • the set() X starts with 100, then jumps to 1000, then jumps to 0, then 100. It should jump from 1000 to 100.
    • the fromTo() works as expected, it starts with x:100, then jumps to x:200 and then jumps back to x:100.

     

    See the Pen JwqJmY?editors=0010 by mm00 (@mm00) on CodePen

     

     

    var originalTl = new TimelineLite({
      onComplete: function() {
        originalTl.pause(0);
      }
    });
    
    originalTl.set("div", { x: 100, immediateRender: true }, 3);
    originalTl.fromTo("div", 0.0001, { y: 100 }, { y: 100 });
    
    originalTl.set("div", { x: 1000 }, 3);
    originalTl.fromTo("div", 0.0001, { y: 200 }, { y: 200, immediateRender:false });
    
    originalTl.set({}, {}, '+=2');

     

     

     

    Ps.: Yeah, I know this example makes no sense, it is just to illustrate the problem. I have nested timelines in my app and one of the nested timelines which has shifted position has a .set()  with immediateRender:true

  11. 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

     

    See the Pen EGGpXW?editors=0010 by mm00 (@mm00) on CodePen

     

     

    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;
    });

     

  12. Thanks Jack!

     

    I was able to create a Codepen as I know the reason: 

    See the Pen qLJevO?editors=0010 by anon (@anon) on CodePen

    1. Open up the console
    2. 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
    3. 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: 

    See the Pen ebQOVN?editors=1010 by anon (@anon) on CodePen

     

    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.

     

  13. 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;

     

  14. 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.

     

     

     

     

     

  15. 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:

    See the Pen QzZZpo?editors=0110 by anon (@anon) on CodePen

     

     

    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.

  16. 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 :) )

    • Like 1
  17. 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.

    See the Pen QzVYyP?editors=0010 by mm00 (@mm00) on CodePen

     

    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?

     

    See the Pen oJPmGp?editors=0010 by mm00 (@mm00) on CodePen

     

  18. 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

    vGCNRrt.png

     

    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:

    11STuHX.png

     

    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 :)

×
×
  • Create New...