Jump to content


circles from size 0 to fully cover the screen – Performance and Concept

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

1) Firstly..

.. I have a huge performance issue with getting plain circles to grow from zero to fully cover the page.

(I am not expecting it to be super smooth on phones; I use stepped animation there; I'm talking performance issues on my fully blown late 2013 15'' MBPr – latest Chrome on El Capitan)


http://beta.richmodis.de (not ready yet by any means!)

Press "Ja" to enable snapscrolling.


The performance issue mainly occurs in the "text-bubble-transition" seen from Panel 2 > Panel 3.


I tried a lot of approaches:

1) svg circles

2) circles done with border-radius and assuring aspect ratio with an pseudo-element padding-top: 100% trick

3) images with an svg circle (embedded base64 style)


And tried to animate it with

1) scale from 0

2) width from 0


The beta contains circle approach 3 (img referencing base64 svg) and animating the width.


Every single approach has huge lags as soon as the circles come up.

And I don't know why.


The only reason I can come up with is that the Easing is just too fast for an animation spreading over the whole viewport.

But then on the other hand, the bubbles, which are way more complex in shape etc., also cover a great portion of the viewport and also have pretty aggressive easing, are much smoother.


Please help me, guys :(



2) Secondly.. 

.. I need some clever math to let the circles scale the circles just until the screen is fully covered, so the following animation can start immediately. (negative offsets don't help here, because I have to hide the div where the animation of the circles is happening in, with negative offset of the following animation would already animate when the overlay is still visible.)

Link to comment
Share on other sites

Hello kreativzirkel


Im not seeing what you describe. I checked latest Firefox and Chrome on Windows 7 and 10. But did not see what you describe.

  • What browser and browser version are you seeing this in?
  • What OS version are you seeing this in?

In the forums its best to provide a reduced codepen with just the tweens giving you trouble. This way we can help you by seeing the issue clearly in a live editable environment like codepen. This way we dont have to debug your whole website that we cant edit.



Thank You

Link to comment
Share on other sites

Like Jonathan said, you'll need to create a reduced demo with just the effect that is giving you a problem.

The circle animation happens very fast and it isn't practical for us to try to scroll through the site to try to trigger it and then dig through a bunch of code to figure out what you are doing.


This demo from Blake is the best I've seen for great performance when needing to have a circle grow to cover the screen. 




It also contains the formula for figuring out how big the circle needs to get.


function maxDistance(x, y) {
  var point = { x: x, y: y };
  var da = distanceSq(point, { x: 0, y: 0 });
  var db = distanceSq(point, { x: width, y: 0 });
  var dc = distanceSq(point, { x: width, y: height });
  var dd = distanceSq(point, { x: 0, y: height });  
  return Math.sqrt(Math.max(da, db, dc, dd));

The maxDistance() function is used to set the radius of the circle based on the greatest distance between where you clicked and any corner of the viewport.

In your case you can probably just use a radius based on the distance between the center of the screen and any corner.

  • Like 3
Link to comment
Share on other sites

Thanks for the quick answers, like always–


I'm experiencing the issues on almost all devices sooner or later. When I restart my MacBook and only have Chrome the overall performance is fluid like Justin crying his river. But even then 1 out of 10 times the end has a little lag. But then on other devices, restarted or not, the performance of the ripple is insanely bad.


I didn't see much sense in making a codepen example hoping that this codepen would produce the same lag.

I'm sure the scale alone in CodePen would work great across everything. So I thought the living use case would make it easier for you to help me.


But you are right; always refreshing and going the page along isn't ideal to debug for you guys.


The CodePen ripple example Carl provided looks promising. I'll look into that before making a dedicated demo myself.



Until then: Why is scaling (in what way ever) an performance issue when 3d rotations with transformPerspectives, transformOrigins with z values etc. pp. are working like a charm?

Link to comment
Share on other sites

kreativzirkel You asked:


Why is scaling (in what way ever) an performance issue when 3d rotations with transformPerspectives, transformOrigins with z values etc. pp. are working like a charm?

It all depends on what you see in the browser inspector, whether GSAP is using matrix() or matrix3d() for the transform CSS property. Transform matrix3() will tell the browser to use hardware acceleration (GPU). When GSAP uses matrix3d() you should get better performance. But it depends what browser your using, since webkit based browsers like Chrome have other gotchas that can cause some bad performance with 3D transforms. Causing blurry text and images or elements shaking and stuttering due to Chrome bugs on how they implement the CSS 3d transforms.


Most of the time its just simply adding properties it expects, such as backface-visibility, transform-style, perspective or transformPerspective so the browser knows how to render your CSS 3d transform correctly. Most of the time i see bad performance, stuttering, or jank due to a browser bug. Also sometimes slight rotations (rotation:0.01) of the transform can help to tell GSAP to use matrix3d(). But even most of those browser bugs can be squashed by including the right CSS properties. Since sometimes the omission of some of those CSS properties i listed above can cause bad performance or trigger the bug. Think of some of those properties as a requirement so you make sure you let the browser know its rendering a 3d transform.



Link to comment
Share on other sites

Interesting Jonathan,


but why can't I just "force" GSAP to use 3d-matrices right from the start? (I assume there isn't, because you would've already told me)




But even most of those browser bugs can be squashed by including the right CSS properties.


And how do I know what the "right CSS properties" are?


In my particular case GSAP decided to go with a 2d matrix

Link to comment
Share on other sites

kreativzirkel just as a rule of thumb.. if you are animating a 3d transform or even a 2d transform and you are rotating, then you should include the following so the browser knows your intent for 3d

Also you might need to bring the parent or the parents parent of the transformed element onto a new rendering layer in webkit based browsers to prevent flickering or stuttering. But it all depends on whats being animated and how.


But sometimes there are instances where the above wont work then in that case it is a bug that can only be fixed by the browser dev team.


Again you will have to test but i add those as a standard to prevent wonky rendering and reduce debugging time.



  • Like 1
Link to comment
Share on other sites


This demo from Blake is the best I've seen for great performance when needing to have a circle grow to cover the screen. 


See the Pen eNrQqV?editors=0010 by osublake (@osublake) on CodePen


It also contains the formula for figuring out how big the circle needs to get.



I came up with a simpler way to find the furthest corner. If the x click distance is greater than half the width, the furthest corner is on the left side. If the y click distance is greater than half the height, the furthest corner is on the top side. You can now single out the furthest corner based on that information.


Revised demo...

See the Pen fe1e511b8505fa9d75d4d9c3cb866f7d?editors=0010 by osublake (@osublake) on CodePen


With some material design ripples...

See the Pen 370fff8b50383668a2118e21f46f7236?editors=0010 by osublake (@osublake) on CodePen


  • Like 5
Link to comment
Share on other sites

The new distance calculation is very cool. Thanks for explaining it.

  • Like 1
Link to comment
Share on other sites

Wow, great intel.



Would you add these properties by default in the CSS or when needed with GSAP?


If the following request is too much to ask, just ignore it.

But I absolutely don't understand OSUblakes CodePen. The example Carl provided made sense to me, until the onUpdate part. I just don't see what this is doing. Why ripple needs to be bound? 

onUpdate: drawRipple.bind(ripple)

I'd highly appreciate an explanation. Maybe on a bare bone level. All I need is 2 circles coming from the middle and ending right when the screen is filled. 

Bonus question: How would you draw everything but the circle in the middle for revealing the new panel? Kind a the same, but inverted / masking– u know :D







PS: How do you link to a user? Neat feature 8)

Link to comment
Share on other sites

Hey kreativzirkel,


You can link to a by copy paste their username directly, the forum does copy the whole HTML tag. Or you can just copy the target and make a normal link.


As for the bind, it is necessary because: scope. Tweens and Timelines (Max/Lite) create their own scope. If you really look at it, who's calling onUpdate? The Tween(Timeline), therefore, this refers to the Tween(Timeline).


If you see in the drawRipple method, it's using things like this.x, this.y - If the onUpdate call were not bound to ripple, this would refer to the Tween(Timeline) and not to the bound ripple object.


Scope has caused many of my brain cells to die but the ones that survived have become stronger. Hopefully, you won't lose as much grey matter as I did.

  • Like 2
Link to comment
Share on other sites

I've always done the user link by clicking on the Special BBCode toolbox icon, and then selecting member.


Just like Mr. Dipscom said, bind changes the scope. Basically what 'this' refers to in a function. So in jQuery, 'this' refers to the element. In GSAP, 'this' refers to the tween/timeline, but I wanted 'this' to refer the ripple, so I used the bind method for a function.


I could have also let GSAP set the scope for me, and it would work the same.

var tl = new TimelineMax({ onUpdate: drawRipple, onComplete: removeCanvas, callbackScope: ripple }) 

And I just posted how to get the radius for a circle expanding from the center over here...




  • Like 2
Link to comment
Share on other sites

Wooooh, exactly this effect, not triggered with a second click, but exactly when the screen is filled, or maybe a little bit earlier. But that shouldn't be a problem, I guess. I will look into this–


This advanced javascript mixed with canvas (never used before) mixed with GSAP (still not second nature to me) is really challanging.


I figured out the this. thing inside the drawRipple() myself, but have never seen the bind function before. I will need to look into this a bit more.

Link to comment
Share on other sites

Understanding scope (this) in JavaScript is definitely the hardest part of it. Even experts from other programming languages have a hard time understanding it. Take that

See the Pen 336146a07bb599bb5c895d860fbc4615?editors=0010 by osublake (@osublake) on CodePen

I made for example. The 'this' I'm return returning in the proxy object is not the same 'this' I'm returning at the end of the method. If you're wondering why I'm returning 'this', it's because it allows you to do method chaining. 


Setting the scope for an function can be done with bind, call, or apply. Bind returns a function with the scope set, so you can call it over and over again. Call and apply, have to be done every time you want to call a function with a scope. See how I'm using all three in this demo...

See the Pen 98c44df7047f45c805648cfeebe9dba6?editors=0010 by osublake (@osublake) on CodePen


And jQuery has a method just like bind, called .proxy().


Working with canvas may seem very confusing at first. It's not hard, just different. You don't work elements, but a context object that you issue drawing commands to. Here's a good post that explains some basics for working with GSAP and canvas.



Also, I didn't put this in my demo, but for production you should scale the canvas context to adapt to different pixel ratios, like on a retina display. Otherwise, it won't look sharp, and will have twice as many pixels, hurting performance. Here's a revised version showing how to do that.

See the Pen 8bc7fa1b97c1bc8a193e6dc99586d8fe?editors=0010 by osublake (@osublake) on CodePen



  • Like 3
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.