Jump to content
Search Community

Hide target by default when not being animated?

QACollective test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Firstly, congratulations on a fantastic library.  It works exceptionally well, is easy to use, logical and well documented!  That's more than I can say for a lot of other technology I'm using ?

 

I have a server side engine streaming GSAP animation commands to the browser in order to overlay information on a video.  Everything seems to be coming together quite well so far.


The problem I have is that once an overlay animation is finished, it's remnant target object is left on screen.

 

So my question: is it possible to default animation targets to only be displayed while they're being animated?

 

I'm trying to keep the code in the browser relatively simple: it just funnels server side commands into GSAP and syncs the animation and video play heads.  Due to the nature of real-time streaming, I don't know when an animation is going to end exactly, and it may end and then resume many minutes later.  I'm trying to avoid having to track state of an animation on the client and hide the overlay, but it seems I may have to do that?

 

Thanks in advance!

See the Pen wOPEqv by anon (@anon) on CodePen

Link to comment
Share on other sites

Glad to hear you're enjoying the toolset so far, @QACollective! Welcome to the forums.

 

You could certainly ad a short autoAlpha tween to the end of your timeline(s) to hide things. In case you're not familiar with it, "autoAlpha" is just a way to animate the opacity AND visibility CSS properties such that when opacity hits 0, visibility gets set to "hidden", and whenever opacity isn't 0, it's set to "inherit". So, for example:

var tl = new TimelineLite();
tl.to(...); //your animations (whatever they are)
...
tl.to("#overlay", 0.3, {autoAlpha:0}); //it'll fade out and hide 

 

That doesn't actually remove anything from the DOM, of course - it simply hides it which improves rendering performance in the browser. 

 

If your goal is to actually remove things from the DOM, you could do that in an onComplete callback or something. Let us know if you need help with that. A codepen demo goes a LONG way in getting help around here just because it helps us to quickly see what's going on. No need to use your actual code - the simplest possible test case is great. 

 

Happy tweening!

  • Thanks 1
Link to comment
Share on other sites

Hello,

 

Thanks for a useful response.  I can see why a CodePen goes a long way, for I fear I've not been articulate enough, sorry.  I've attempted to add one that helps.

 

My goal is simply to not display an element unless it is being animated, when my code doesn't know where animations are expected to start or stop at the time to() is called, because that is simply an absence of animation commands coming from the server.

 

So in essence, I want to set a default for, say, opacity to be 0 unless otherwise animated by GSAP.  I'd love for that to be possible!

 

Initially, I set the animation targets to have negative top and left css values.  But after that, boxes are left on screen when there is no animation taking place.

 

If this isn't possible as a built-in feature, will the next best idea be to loop through all available tween elements to check TweenMax.isTweening() and then set the corresponding element's display to 'none' or 'inherit' accordingly and run that using setInterval?

Link to comment
Share on other sites

 

Hi @Dipscom,

 

I'm much enthused by the great advice given so readily on this forum, thank you.

 

That's a nifty solution, but I fear it would create too many tweens - because, despite my demo, each of my animations only lasts 0.04 seconds (1 frame @ 25 fps).  The overall timeline may last at least an hour.  So I'm reticent to triple the number of tweens simply to achieve this.

 

I have been playing around and managed to get the code below to work.  It's crude, but it stops my JS from having to read deeply into the JSON payload coming from the server and track object state, so I'll probably keep it unless I can find something better.

 

I'm very much interested in performance so thanks for the tips on using x and y in stead of left and top, I'll make changes to the server to send those attribute names through in stead.

 

Do you have any other pearls of wisdom on performance, particularly in the most performant way to hide an element from view?  As you can see, I'm currently using opacity - but that can easily be changed.  I've already set TweenMax.ticker.fps(25) to match the video.

 

setTimeout(hideInactiveOverlays, 10);

function hideInactiveOverlays(){
	for (var target in markups_animation)
		if (!TweenMax.isTweening(markups_animation[target]) && markups_animation[target].style.opacity != "0")
			markups_animation[target].style.opacity = "0";

	setTimeout(hideInactiveOverlays, 10);
	
}

 

Link to comment
Share on other sites

42 minutes ago, QACollective said:

That's a nifty solution, but I fear it would create too many tweens - because, despite my demo, each of my animations only lasts 0.04 seconds (1 frame @ 25 fps).  The overall timeline may last at least an hour.  So I'm reticent to triple the number of tweens simply to achieve this.

 

You can always set the opacity of the element to 0 and use clearProps on it.

 

See the Pen VRQPxP?editors=0010 by dipscom (@dipscom) on CodePen

 

But you still would have an issue of the time it takes to animate in, 0.04s, is so short that whatever you have on screen will be gone before the user can even perceive it. I am having a very hard time imagining a reason why you even would have an animation running on such short span of time.

 

Help me understand it and I think we can offer more tips and ideas on how to improve it.

 

There's nothing more performant than using opacity and transforms in the DOM. You could toggle the visibility as well to stop mouse events triggering but that's pretty much about it.

 

What would concern me in this hour long video, with overlays being fired at 0.04s is how many elements you will have in your DOM tree... You might want to remove those after you shown them. Unless, of course, you need the ability to rewind the video as well.

 

  • Like 4
Link to comment
Share on other sites

Like @Dipscom, I'm concerned about the 0.04 second "animations" - it seems wasteful because it's so short that nobody will notice any animation. 

 

I'm also curious why you're dropping the FPS to 25. Beware that when you animate position (x/y or top/left), it's very easy for people to notice jerky animations when the frame rate is somewhat low. If it were me, I'd probably leave it at the native 60fps and also make your animation duration a tad longer so that people actually notice it. 

 

From a performance perspective, there are a lot of question marks in my mind about what you're trying to accomplish (and why). 

  1. If these animations are up to an hour long, is your goal to allow users to scrub to any point on the timeline and have the DOM elements show up accordingly? 
  2. How many of these overlays are you creating? And are you reusing any or just generating a new one every single time? 

I wouldn't recommend simply setting opacity to 0 on things you don't want to see because that still leaves the elements in the DOM for rendering and mouse events. I'd at least set visibility:hidden so the browser can ignore them rendering-wise and mouse event-wise. 

 

I'm not a fan of the whole setTimeout() 100 times per second. Seems very wasteful performance-wise, and it's not even synchronized with screen refreshes. Believe it or not, it'd likely be much more performant to have the extra autoAlpha tweens in the timeline per @Dipscom's advice. 

  • Like 2
Link to comment
Share on other sites

10 hours ago, QACollective said:

I'm much enthused by the great advice given so readily on this forum, thank you.

 

I did miss the opportunity to mention that is always an honour to be dealing with individuals who have a vocabulary so refined. Specially when sentences using words such as 'enthused' are followed with others containing 'nifty'.

 

:D

 

  • Thanks 1
  • Haha 1
Link to comment
Share on other sites

My apologies to both @Dipscom and @GreenSock.  I rarely post to forums because I'm accustomed to finding answers myself before needing to hassle people!  I rarely come across a technical problem without internet precedent and at least a pointer to a solution. 

 

Clearly, my skills in detailing a problem aren't as honed as my problem solving skills because I've probably been overzealous in my effort to simplify the problem description for this forum. ?

 

Some additional details which I hope will clarify my problem:

  • My application in general terms is the annotation of live video
  • The number of annotations (which is equal to the number of DOM elements) will typically not be high ... say an absolute maximum of 500, but those annotations are being animated and hidden regularly (reused)
  • Animation commands are being received by JS code from the server on a per frame basis
  • Frame rate is calculated on a per video basis, but is typically 25fps, so 1/25 = 0.04 seconds per frame
  • Recap: the code in the browser is currently completely naive on what's contained in the JSON coming from the server, it just receives the JSON and plugs it into the to() function, so far I've avoided having to make the code aware of the visibility and duration of annotation objects

Here is a sample of the JSON received for one annotation object on one frame:

 

{id: 11, markup_name: "dangerous_object_reticle", markup_from: "VisualObjectDetector", markup_type: "overlay", markup_text: "{"target": "acd338db-9443-42d7-9fd2-2d5107ddd75e",…h": "78px", "height": "69px"}, "position": 24.96}"}

 

The actual JSON that will be parsed and passed to the to() function is in markup_text:

 

{"target":"acd338db-9443-42d7-9fd2-2d5107ddd75e","duration":0.04,"vars":{"opacity":1,"y":61,"x":197,"width":"78px","height":"69px"},"position":24.96}

 

I'm grateful for the 'take-aways' so far though:

  • Factory functions can be very useful for applying complex but routine animations
  • x / y transforms are more performant than left / top
  • opacity is the most performant for the situation where you will re-use DOM objects

 

So far, as I see it, I've got three options:

  1. Factory function as per @Dipscom's earlier post, but with the detail in this post may no longer be feasible.
  2. More fully develop out my hideInactiveOverlays solution
  3. Make the code that's transferring animation from server to GreenSock track the annotation objects, detect a ~3 frame absence of animation and automatically tween opacity to 0 in 1 frame.

 

Just confirming, there is no 'onTweenStart' and 'onTweenFinish' type events?  God, I hope I've not missed them in the documentation!

 

QA

Link to comment
Share on other sites

2 hours ago, Dipscom said:

 

I did miss the opportunity to mention that is always an honour to be dealing with individuals who have a vocabulary so refined. Specially when sentences using words such as 'enthused' are followed with others containing 'nifty'.

 

:D

 

 

Wow, thank you for saying that!  I do take pride in at least attempting to be articulate.  The sentiment is indeed mutual, as I find both the community here and GreenSock as a product very welcoming, well considered and refined.  I've always noticed there is both art and wisdom in concision, so on that note I'll hightail on outta here.

 

?

  • Haha 1
Link to comment
Share on other sites

3 hours ago, QACollective said:

Just confirming, there is no 'onTweenStart' and 'onTweenFinish' type events?  God, I hope I've not missed them in the documentation!

 

Absolutely. There are onStart and onComplete callbacks you can set for any tween or timeline (or both). They should be all over the docs, wherever the special properties are listed for the "vars" object (TweenLite, TweenMax, all the to(), from(), fromTo() methods, etc.) ;)

 

3 hours ago, QACollective said:

so far I've avoided having to make the code aware of the visibility and duration of annotation objects

 

Are you saying that when you display an annotation (overlay), you have no idea how long it needs to stay there? Like...the duration is unknown because it's a live feed and decisions must be made on-the-fly? If so, what determines when they go away? 

 

I don't think I got an answer to my earlier question: "If these animations are up to an hour long, is your goal to allow users to scrub to any point on the timeline and have the DOM elements show up accordingly? "

 

From what I can tell, this should be relatively simple to set up a reusable function that takes care of positioning, setting the text, and fading in an overlay, adding it to your timeline at the correct time, and then another function that handles fading it out at the right spot in the timeline. You could probably even reuse a small set of DOM elements for ALL the overlays (rather than having 500 of them, mostly sitting dormant). Like if you always have a maximum of 2 or 3 overlays on the screen at any given time, maybe you've got 5 <div>s that get cycled through over and over. It'd look the same, but be far less burdensome on the browser. Just a thought. 

  • Like 1
Link to comment
Share on other sites

Having a tree way conversation is hard...

 

I have comments on both of your responses.

 

8 hours ago, GreenSock said:

Are you saying that when you display an annotation (overlay), you have no idea how long it needs to stay there? Like...the duration is unknown because it's a live feed and decisions must be made on-the-fly? If so, what determines when they go away?

 

I think you've misunderstood him, Jack. I think he is saying he deliberately has not included the logic that removes the annotation for simplicity sake. Which, in turn is causing the two of us to be confused as to why have a 0.04s tween that then on completion gets rid of itself.

 

8 hours ago, GreenSock said:

I don't think I got an answer to my earlier question: "If these animations are up to an hour long, is your goal to allow users to scrub to any point on the timeline and have the DOM elements show up accordingly? "

 

It's a live video, I will assume it's like TV, no scrubbing, no pausing, no control. It just plays its thing. His own words bellow.

 

12 hours ago, QACollective said:

My application in general terms is the annotation of live video

 

Now, mr. QA, your turn.

 

As much as you have been trying to be clear I think your attempts to simplify the example has caused more confusing than has helped. Give us a bird's eye view of what exactly is that you need to achieve. Here's my interpretation:

  • Live streaming of a video.
  • Somewhere someone inputs an annotation and saves it on the server (how one would know ahead of time what to write on a live video is beyond me but, let's carry on).
  • This annoation shows on screen for a set amount of time (surely not 0.04s).
  • Afterwards it disappears.
  • At some other point, another annotation is created and the cycle restarts.

If this is correct, the following questions apply:

  • Why create a timeline that holds the entire video? Unless the premise of it being a live streamed video is incorrect.
  • How does the annotation know when to go away?
  • What do you mean exactly when you speak of animation (see the quote bellow)? The animation ends after 0.04 seconds (one frame in your video fps), so, it's nothing but a blip on screen if you follow your own description of desired behaviour.

With the assumptions made above one can recommend the following:

  • There is no need to have a .to() tween of 0.04s. You can use a .set() tween, as you seem to be using GSAP to position your annotation on screen, not actually animate anything.
  • As Jack said, you probably don't even need 500 DOM elements to hold the annotations. Only the maximum number of annotations that will ever be on visible at one time. If that's one, you only really need a single DOM annotation element.
  • You should also use 'autoAlpha' over 'opacity' as it will toggle the 'visibility' property as well as changing the opacity, decreasing the amount of work (as minimal as it will be) when working out what's not to render.
  • If the annotation is to show up, hold a set (the same) amount of time and then go away, you are better off creating a factory that will create a timeline with such behaviour but nothave it attached it to a reference, thus, flushing everything once it has run.

Ultimately, the question posed in the title of the thread has been answered. We've shown you how to hide the element as soon as the tween is over. However, it is clear that is not your issue but something completely different. Again, help us understand and we shall share with you any ideas, techniques we have.

  • Like 2
Link to comment
Share on other sites

1 hour ago, Dipscom said:

Having a tree way conversation is hard...

 

True!  Since you unified several important parts of both yours and Jack's thinking, I'll respond mostly to your quotes, except the following.

 

7 hours ago, GreenSock said:

Absolutely. There are onStart and onComplete callbacks you can set for any tween or timeline (or both). They should be all over the docs, wherever the special properties are listed for the "vars" object (TweenLite, TweenMax, all the to(), from(), fromTo() methods, etc.) ;)

 

Crud.  I really shouldn't have missed that, but alas I did.  Sorry.  So while running inside an onComplete function, if I can determine that the current tween doesn't have a successor in the next, say, 5 frames, I could set its opacity to 0.

 

7 hours ago, GreenSock said:

Are you saying that when you display an annotation (overlay), you have no idea how long it needs to stay there? Like...the duration is unknown because it's a live feed and decisions must be made on-the-fly? If so, what determines when they go away? 

1 hour ago, Dipscom said:

I think you've misunderstood him, Jack. I think he is saying he deliberately has not included the logic that removes the annotation for simplicity sake. Which, in turn is causing the two of us to be confused as to why have a 0.04s tween that then on completion gets rid of itself.

 

You are both on the right track.  When I receive animation instructions for an annotation in a frame of video, I can only know for sure it needs to be there for 0.04 seconds from the position time (also in seconds).  For simplicity's sake, I am keeping the JS on the client 'dumb' ... it just copies animation instructions over into GSAP.  I can only know when a tween needs to go away when I stop receiving animation instructions for a certain annotation.  At the moment I don't do that kind of tracking in the browser.

 

 

7 hours ago, GreenSock said:

I don't think I got an answer to my earlier question: "If these animations are up to an hour long, is your goal to allow users to scrub to any point on the timeline and have the DOM elements show up accordingly? "

 

1 hour ago, Dipscom said:

It's a live video, I will assume it's like TV, no scrubbing, no pausing, no control. It just plays its thing. His own words bellow.

 

Again, you're both on the right track.  The animation instructions are indeed being produced on a completely live feed, which has the constraints that @Dipscom mentions.  However, these animation instructions are stored verbatim in a database for later viewing of the live feed with the same annotations.  So when a live video is being played back I also plan on being able to support seeking to any point.  So I'm trying to code a viewer that will work equally well in both scenarios with the same coding paradigm. I've basically captured all the HTML5 video's events and matched them up to GSAP TimelineMax calls, which seems to work quite well - the events matched up nicely, which doesn't seem like coincidence ... kudos guys :)

 

1 hour ago, Dipscom said:

As much as you have been trying to be clear I think your attempts to simplify the example has caused more confusing than has helped. Give us a bird's eye view of what exactly is that you need to achieve.

Yeah, I think you're right on this count.  Sorry for this.  I really hate to waste good people's time ?

 

1 hour ago, Dipscom said:
  • Live streaming of a video.
  • Somewhere someone inputs an annotation and saves it on the server (how one would know ahead of time what to write on a live video is beyond me but, let's carry on).
  • This annoation shows on screen for a set amount of time (surely not 0.04s).
  • Afterwards it disappears.
  • At some other point, another annotation is created and the cycle restarts.

This is mostly correct, except the last two points. The annotation is generated using AI and I think the key you might be missing here is that even though one animation only lasts 0.04 seconds, I'd completely expect there to be multiple consecutive  animations of that duration for each target.  So even though each animation lasts only 1 frame (0.04 seconds), the multiple consecutive animations give the impression of continuity.  But the other major point (which I think you already understand) is because of the live scenario, I don't know the duration or even the x,y coordinates of the target until I receive the next frame.

 

1 hour ago, Dipscom said:

Ultimately, the question posed in the title of the thread has been answered. We've shown you how to hid the element as soon as the tween is over. However, it is clear that is not your issue but something completely different. Again, help us understand and we shall share with you any ideas, techniques we have.

You're right - and I can't apologise enough, sorry.  It's also annoying that I can't be completely open with you due to the fact that what I'm doing is both sensitive in nature and expected to be an industry first.

 

Thank you both for your hard work (mostly on my poor explanation of a clearly evolving problem) and perseverance!

 

QA

 

  • Like 1
Link to comment
Share on other sites

There are some bits to go on with here.

 

First I think you should stop referring to those tweens as 'animation'. You don't want an animation, there is no animation. There's the placement of the annotation and whether it is visible or not.

 

1 hour ago, QACollective said:

The annotation is generated using AI and I think the key you might be missing here is that even though one animation only lasts 0.04 seconds, I'd completely expect there to be multiple consecutive  animations of that duration for each target.  So even though each animation lasts only 1 frame (0.04 seconds), the multiple consecutive animations give the impression of continuity.

 

Another point to raise: This 0.04s tween here. In the above scenario, you will have a flickering annotation if you are hiding it as soon as the tween is complete. As, it will always be tweening from opacity 0 to 1 and no matter what you do, probably 0.01s of the 0.04s the element will have an opacity of 0.

 

1 hour ago, QACollective said:

if I can determine that the current tween doesn't have a successor in the next, say, 5 frames, I could set its opacity to 0.

 

I really can't see how that would happen. Inside the callback of the onComplete, how could you possibly know the status 5 frames in the future? Once the callback fires, the future has not yet arrived.

 

Maybe, what you want to do is have the server send down a message to hide the annotation. It really feels to me it will be a much easier solution to implement than predicting the future... You json payload, that you say is already constantly arriving from the server would have two versions, one being "show annotation id whatever", another "hide annotation id whatever" or even, if you follow our suggestion of reusing the same DOM element to hold all annotations: "hide whatever is on screen".

  • Like 1
Link to comment
Share on other sites

1 hour ago, QACollective said:

It's also annoying that I can't be completely open with you due to the fact that what I'm doing is both sensitive in nature and expected to be an industry first.

 

Sometimes it's like playing charade, hard mode on....

  • Like 2
Link to comment
Share on other sites

Yeah, I think @Dipscom and I are on the same page. If I were you, I'd probably handle the positioning and the opacity/visibility with separate logic because positioning is frame-by-frame exact, but opacity/visibility is more gradual, like "fade in over 0.2 seconds...and wait until 5 frames elapse without any new instructions for the target object at which point we'll fade out over 0.2 seconds" (or something like that). 

 

So you could have logic that tracks just that piece - when instructions arrive for a new target, I shove an autoAlpha tween into the timeline and then I use a separate delayedCall() with a duration equivalent to whatever your timeout is (5 frames?) that, when completed, will shove a fade-out autoAlpha into the timeline. Then, every time I get a new set of instructions that does include that target, I restart() the corresponding delayedCall() so that it kinda resets the timer. Sorta like:

var lookup = {}; //a lookup table so we can find elements by ID

function getNewData(data) {
    var id, p, element;
    
    //I have no idea how your data is formatted, but basically loop through it...
    for (p in data) {
        id = data[p].id;
        element = lookup[id];
        if (!element) {
            //there's no id in the lookup, so we need to start an overlay
            element = startOverlay(data[p]);
        } else {
            //we received data for this element, so restart its timeout
            element.timeout.restart();
        }
        //animate the positional data.
        master.to(element, 0.04, {x:data.x, y:data.y}, master.time());
    }
}

function startOverlay(data) {
    //I'm not sure how you're managing your elements, so I'm just creating a new one here but you'd probably grab one from a group of unused ones that you recycle (beyond the scope of this sample)
    var element = document.createElement("div");
    document.body.appendChild(element);
    
    //add into the timeline at the current time.
    master.fromTo(element, 0.2, {autoAlpha:0}, {autoAlpha:1}, master.time());
    
    //record the ID/element in the lookup table
    lookup[data.id] = element;
    
    //start a timeout and attach it to the element so we can easily find it. 
    element.timeout = TweenLite.delayedCall(0.5, fadeOut, [data.id]);
    
    return element;
}

function fadeOut(id) {
    //find the element by ID
    var element = lookup[id];
    
    //fade it out starting at the current time in the master timeline
    master.to(element, 0.2, {autoAlpha:0}, master.time());
  
    //this is also where you could recycle things, remove them from the lookup if you want, and basically flag this thing as not on the screen anymore (well, after the autoAlpha animation is done, so maybe in an onComplete)
}

 

At least that's a rough sketch of what I had in my mind. Not sure if it's helpful or not. 

 

Happy tweening!

  • Like 1
Link to comment
Share on other sites

9 hours ago, Dipscom said:

Another point to raise: This 0.04s tween here. In the above scenario, you will have a flickering annotation if you are hiding it as soon as the tween is complete.

Correct - this is why I tried writing that hideInactiveOverlays function after you kindly provided the factory function solution.  I figured that there would be flickering if I did this.

 

9 hours ago, Dipscom said:

I really can't see how that would happen. Inside the callback of the onComplete, how could you possibly know the status 5 frames in the future? Once the callback fires, the future has not yet arrived.

Yep, basically you're right. Unless I introduce a 5-10 frame buffer for the purposes of solving this problem.  Hmm...I'll add that to the possibilities list, down the bottom ?

 

9 hours ago, Dipscom said:

Maybe, what you want to do is have the server send down a message to hide the annotation. It really feels to me it will be a much easier solution to implement than predicting the future... You json payload, that you say is already constantly arriving from the server would have two versions, one being "show annotation id whatever", another "hide annotation id whatever" or even, if you follow our suggestion of reusing the same DOM element to hold all annotations: "hide whatever is on screen".

This is a red hot option, although would be a significant re-code server side.  It may be worth it in the long run though because I could hide things with more certainty.  Thanks, I'll look into that.

 

3 hours ago, GreenSock said:

So you could have logic that tracks just that piece - when instructions arrive for a new target, I shove an autoAlpha tween into the timeline and then I use a separate delayedCall() with a duration equivalent to whatever your timeout is (5 frames?) that, when completed, will shove a fade-out autoAlpha into the timeline. Then, every time I get a new set of instructions that does include that target, I restart() the corresponding delayedCall() so that it kinda resets the timer. Sorta like:

Wow.  Okay, so this is cool.  Not only does this validate the way I've been doing things on my end - as a lot of that code resembles my own, but its also provided significant education on what's possible using delayedCall() in respect to the timeline.

 

I'll re-group, re-code and reply when I've got a working solution to let you know how I went.

 

Thank you so much @Dipscom and @GreenSock for your perseverance and sagely advice! ?

 

QA

 

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...