Jump to content
Search Community

multiple zoom site based on scroll

Sublimio test
Moderator Tag

Recommended Posts

Hi all,

I have to zoom the entire site based on how much I scrolled. From 1900 pixels for 500 pixels I have to apply a zoom factor of 1.2. From pixel 3000 for 500 pixels I have to apply a zoom factor of 7. I change the origin of the transformation according to the point where I want to focus the zoom. Everything works but when I get to the second animation I don't have a zoom from 1.2 to 7 but the zoom restarts from 1. I can I solve?



See the Pen mdaJNrj by marco-catone (@marco-catone) on CodePen

Link to comment
Share on other sites

If you want things to happen in tandem with each other it is best to use a timeline and put your animations on that timeline and then let one ScrollTrigger manage that timeline. Having multiple timeline control the same element can be done, but it has a lot of gotchas, which can be hard to manage if you're new to the tools and I would not recommend.


What I've done is create a timeline and put your two tweens on that timeline and in the middle I've put an tween that does nothing for 1 second. To explain it I've changed the values a bit, but with some calculations you could do the same thing with your specific values. Durations don't matter when working with ScrollTrigger except it matters what the durations are in relation to each other. Right here all durations are the same, so each tween will take up the same amount of space.


In the below example the ScrollTrigger starts at 1900px and ends 3000px later, there are 3 tweens of 1 second each, so 3 / 3000px equals 1000px, so each animation will take up 1000px. 


See the Pen ExGVYPL?editors=0010 by mvaneijgen (@mvaneijgen) on CodePen


If I do some quick math for your example. You want things to happen over 500px then to 3000px plus another 500px, makes 4000px. The two tweens take up 1000px that leaves 3000px. Lets give each tween a duration of 1, which will make 3000px / 500px = 6, so if my calculations are correct


We start at 1900px and stop at +=4000px, the frist tween takes one second, then we do nothing for 6 seconds and then the last tween also takes a second. Math is not my strong suit, so please also calculate these your self, but it would look something like this Hope it helps and happy tweening! 


See the Pen GRPpKvJ?editors=0010 by mvaneijgen (@mvaneijgen) on CodePen




  • Like 1
Link to comment
Share on other sites

I update my example to better explain the problem. Actually I have some elements (texts, images) in the columns and when these elements reach the viewport (or after a certain scroll) I want the whole site to zoom on that element. My problem is that before any animation done the zoom factor of "#wrapContainer" restarts from 1 and doesn't keep its previous state. In these codepen you can see some elements in the columns. Your code works for my previous example, but it seems complicated to update for my future needs. Do you think there may be other ways?


Link to comment
Share on other sites

Let me preface by saying that I mark this topic to get some more eyes on it, so maybe someone else  has a great idea. 


But I would fake it. The scale effect doesn't need to be on the whole page, it just needs to look like it, so I would build sections that connect to each other that you scale up and down. and just make it so that each section is at least 100vh, so that any effect you do is always happening on the whole screen. You then only have to make each section line up, but if you use neat scale numbers that would be easy to do. Then the ScrollTrigger markers can be just one the sections where you want things to happen, so no need to calculate weird offsets. That is how I would do it I don't see a way of getting it with the whole page scrolling, certainly not when everything needs to be also responsive later on. 

Link to comment
Share on other sites

Sorry maybe i just complicated the question or the example. I just want to make it possible to zoom twice on an object at two different times without the zoom factor resetting to 1x.

Let's take this other example:

When the red square enters the viewport the green square scales 2x. When the yellow square enters the viewport the green square scales 7x but, in this example, before the second animation, the scale factor resets to 1x. What is my mistake?


See the Pen MWZaLVO by marco-catone (@marco-catone) on CodePen

Link to comment
Share on other sites

That you can fix with immediateRender: false, on the second tween. GSAP tries to be helpful and setup all the animations before they happen, so it doesn't need to calculate everything on the fly, but this will cause your issue if two different tweens try to animate the same element. I'd tested this on your other pen and this still causes some issues, that is why I didn't share it, but maybe you find a way to work around it. Hope it helps and happy tweening! 


See the Pen ExGVrrO?editors=0010 by mvaneijgen (@mvaneijgen) on CodePen

Link to comment
Share on other sites

Yes, @mvaneijgen is correct. Let me know if you need further clarification about why immediateRender: false is required in that 2nd tween.


The only other problem with your 2nd demo is that you're CHANGING the transformOrigin in the 2nd tween. That'll of course cause a jump because of the logic. You can think of transforms almost like a lens through which the native element is viewed...a filter, or something like that. The native element sits in its native position in the layout, and THEN transforms get applied to make it appear elsewhere. So if you change the origin of those transforms, it can end up being in a totally different position. 


For example, let's say you've got a 100x100 square that's natively lined up perfectly in the top left corner of the viewport and then you apply a scale of 2 with a transformOrigin of "left top". Well, that means the top left corner will stay exactly where it originally was, and all the scaling will push things down and to the right, thus you'll see a 200x200 square that's still aligned in the upper left corner. BUT...


If you just change the transformOrigin to "bottom right" (and leave the scale at 2), the entire square would jump 100 pixels up and to the left because the bottom right corner of the native element would stay pinned and all the scaling would go up and to the left. See what I mean? 


The only way to make that go smoothly would be to keep the transformOrigin consistent and simply offset the x/y transforms to compensate for where they would otherwise be with the altered transformOrigin. Does that make sense? 

Link to comment
Share on other sites

By the way, there's a helper function for this: 

function smoothOriginChange(targets, transformOrigin) {
  gsap.utils.toArray(targets).forEach(function(target) {
    var before = target.getBoundingClientRect();
    gsap.set(target, {transformOrigin: transformOrigin});
    var after = target.getBoundingClientRect();
    gsap.set(target, {x:"+=" + (before.left - after.left), y:"+=" + (before.top - after.top)});

See the Pen feb24447fb7c6a82e777a0110c281a31?editors=0010 by GreenSock (@GreenSock) on CodePen

  • Like 1
Link to comment
Share on other sites

Cool thanks. So basically I have to create the new animation after the previous one is finished. So I can set the new transform origin for the next animation, when I'm scrolling down. But how can I set the previous transform origin if I'm scrolling back?

Link to comment
Share on other sites

  • 2 weeks later...

Sorry, regarding transform: scale, I noticed that in Safari everything gets blurry when I scale too much. It's not about fonts or images, but about the entire screen (even the border of the divs). You can also notice this in the latest codepen. Is this a Safari bug? I searched a lot online but I only found solutions such as:

  • transform-style: preserve-3d;
  • transform: perspective(1px);
  • image-rendering: crisp-edges;

but none solve the problem


See the Pen qBLOePj by GreenSock (@GreenSock) on CodePen


Link to comment
Share on other sites

That's a rendering thing in Safari. When it applies GPU acceleration, think of it like it takes a screenshot of the element at its "native" size and then just stretches those pixels when you scale. 


You can try adding this to the top of your code: 

gsap.set("#wrapContainer", {
  force3D: false

Which disables the GPU acceleration (transforms don't get a 3D one applied if it's not necessary)


Or you can change the way you set things up so that your "native" element size at the biggest value you need, and then scale up from a much smaller value. In other words, instead of scaling 1 -> 10 you can build it as if it's naturally at the 10x size (natively) and scale from 0.1 -> 1. That way, the "screen capture" is much more detailed and high-resolution. 

  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...

I abandoned the idea of using transform:scale because force3d didn't solve the blurred content problem on safari. So I thought of modifying the width of the container and enlarging/moving the internal elements dynamically (I'm using container media query) and moving the Y,X choords of the container to center the focal points on the screen. Everything works but on safari this time I have lag problems. Any advice on improving performance especially on Safari?


this is the actual work in progress



Thanks a lot

Link to comment
Share on other sites

I definitely wouldn't do that. I think it'd be helpful if you tried wrapping your head around how browsers render things and the performance ramifications. Think through how many pixels the browser is forced to re-calculate on each tick. GSAP itself can rip through stuff SUPER fast, but in 99.9% of the cases, the slowdown is due to graphics rendering in the browser. GSAP might take up 0.5% of the resources and the rest might be eaten up by layout and graphics rendering. The key is to make it cheaper on the browser to do all those tasks. Transforms are far, far cheaper for the browser than if you're changing the top/left/width/height of the elements because those properties cause layout reflow (expensive). 


As I said earlier, if you're scaling things up too much, some browsers will pixelate so the solution is to make the native dimensions bigger and adjust your scale values accordingly (I already described this technique in my last post). 


You're asking the browser to fabricate a LOT of pixels on every tick the way you're doing it right now. I'm not surprised Safari is slowing down. Again, it's totally unrelated to GSAP. 


Performance is a very deep topic, but here are some general tips: 

  1. Try setting will-change: transform on the CSS of your moving elements (I know in your case this caused pixelization, but if you employ the last technique I mentioned, it'd likely resolve that). 
  2. Make sure you're animating transforms (like x, y) instead of layout-affecting properties like top/left. 
  3. Definitely avoid using CSS filters or things like blend modes. Those are crazy expensive for browsers to render.
  4. Be very careful about using loading="lazy" on images because it forces the browser to load, process, rasterize and render images WHILE you're scrolling which is not good for performance. 
  5. Make sure you're not doing things on scroll that'd actually change/animate the size of the page itself (like animating the height property of an element in the document flow)
  6. Minimize the area of change. Imagine drawing a rectangle around the total area that pixels change on each tick - the bigger that rectangle, the harder it is on the browser to render. Again, this has nothing to do with GSAP - it's purely about graphics rendering in the browser. So be strategic about how you build your animations and try to keep the areas of change as small as you can.
  7. If you're animating individual parts of SVG graphics, that can be expensive for the browser to render. SVGs have to fabricate every pixel dynamically using math. If it's a static SVG that you're just moving around (the whole thing), that's fine - the browser can rasterize it and just shove those pixels around...but if the guts of an SVG is changing, that's a very different story. 
  8. I'd recommend strategically disabling certain effects/animations and then reload it on your laptop and just see what difference it makes (if any). 

Ultimately there's no silver bullet, like "enable this one property and magically make a super complex, graphics-heavy site run perfectly smoothly even on 8 year old phones" :)


I hope this helps!  💚

  • Like 1
Link to comment
Share on other sites

For those interested I solved the problem by returning to the use of transform:scale with force3D:false but with an addition: I only keep the objects affected by the zoom visible, hiding all the other objects outside the viewport with display:none (not visibility: hidden or opacity:0). This way safari doesn't blur.


p.s. I tried before this solution but Safari Lag with 10x scale too.

On 9/18/2023 at 5:45 PM, GreenSock said:

As I said earlier, if you're scaling things up too much, some browsers will pixelate so the solution is to make the native dimensions bigger and adjust your scale values accordingly (I already described this technique in my last post). 


Link to comment
Share on other sites

Yes, I meant it lags starting with everything 10x native and scaling down.


I have another problem, I don't know if I can continue with this thread or open another one: With all this scaling and moving the parent container in the Y axis, ScrollTrigger loses references on where to start.

Let me explain: seeing this codePen I expect the second animation to start on the top of .box1 but in reality it doesn't work. If I resize the window the start repositions itself correctly.


How can I solve it?  How can I make scrollTrigger always update references? Thanks


See the Pen rNojMNG by marco-catone (@marco-catone) on CodePen

Link to comment
Share on other sites

@Sublimio yes, it'd be much better to start a new thread for a new question like that. 


The problem is that you're animating the element (or parent of the element which of course also animates the element itself) that's used as the trigger. That's almost always a bad idea because it'll throw off all the start/end calculations. What you're asking to do can’t happen automatically. In this particular case you could attempt the calculations manually, like this:

See the Pen poqLJGX?editors=1010 by GreenSock (@GreenSock) on CodePen


I hope that helps. If you still need assistance, please start a new thread. 

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