Jump to content
Search Community

I have an issue with animation being laggy only on iOS Safari.

Danile Yu test
Moderator Tag

Recommended Posts

I have a complicated animation that works well on computers and Android devices. Unfortunately, it is laggy on iOS Safari. Upon investigation, I discovered that the lag only occurs when the Safari menu bar toggles its position up or down. However, when I disable the menu bar on my phone, the animation performs well.
How can I fix this problem, thanks for your help!

Link to comment
Share on other sites

Hi @Danile Yu. It's super difficult to troubleshoot without a minimal demo, but my guess is that you're talking about the normal ScrollTrigger.refresh() that must occur whenever the viewport resizes (which happens when the address bar shows/hides) in order to keep the start/end positions accurate. If you want to skip that recalculation, you can: 

ScrollTrigger.config({ ignoreMobileResize: true });

Just beware that of course the start/end positions won't get recalculated but in some scenarios that's fine. 

 

Does that help? 

 

If you still need assistance, please make sure you provide a minimal demo (like a CodePen) that clearly illustrates the problem so that we can take a peek and see it in context. 👍

Link to comment
Share on other sites

After following your suggestion, the performance of iOS Safari improved slightly. However, it still is not good enough. I tried putting my code on Codepen to create a demo, but it did not work properly. The code contains about 40 canvas DOM elements in canvases1 to canvases4, and the animation involves spreading these canvases with a goat, sorry to post such a long code here.

    function bannerOutAnimation() {

        let bannerOut = gsap.timeline({
            scrollTrigger: {
                trigger: ".second-page",
                start: "top bottom",
                scrub: 1,
                end: `+=${window.innerHeight}`,
                toggleActions: "play none none reverse",
                ignoreMobileResize: true
            },
            force3D: true, // 触发硬件加速
        });
        const canvases1 = gsap.utils.toArray('.addCanvas:nth-of-type(4n+2)');
        const canvases2 = gsap.utils.toArray('.addCanvas:nth-of-type(4n+3)');
        const canvases3 = gsap.utils.toArray('.addCanvas:nth-of-type(4n+4)');
        const canvases4 = gsap.utils.toArray('.addCanvas:nth-of-type(4n+5)');
        bannerOut.to(".banner-paraBox", {
            opacity: 0,
            y: -window.innerHeight / 2,
            duration: 500 / window.innerHeight
        }).to(canvases1, {
            y: (index, target) => {
                let yDistance;
                if (index >= Math.round(blocksNum.x / 2)) {
                    if (index == Math.round(blocksNum.x / 2)) {
                        yDistance = window.innerHeight * 2
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        yDistance = window.innerHeight * 1.8
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        yDistance = window.innerHeight * 1.6
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        yDistance = window.innerHeight * 1.4
                    } else if (index % Math.round(blocksNum.x / 2) == 4) {
                        yDistance = window.innerHeight * 1.2
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        yDistance = window.innerHeight * 1
                    }
                } else {
                    if (index % Math.round(blocksNum.x / 2) == 4) {
                        yDistance = window.innerHeight * 1.8
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        yDistance = window.innerHeight * 1.6
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        yDistance = window.innerHeight * 1.4
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        yDistance = window.innerHeight * 1.2
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        yDistance = window.innerHeight * 1
                    }
                }
                return `-${yDistance}px`;
            },
            duration: 1
        }, "<").to(canvases2, {
            y: (index, target) => {
                let yDistance;
                if (index % 2 == 0) {
                    yDistance = window.innerHeight * 1.6
                } else {
                    yDistance = window.innerHeight * 1.35
                }
                return `-${yDistance}px`;
            },

            duration: 1
        }, "<").fromTo(canvases2, {
            x: 0
        }, {
            x: (index, target) => {
                let xDistance;
                let side = index < Math.round(blocksNum.x / 2) ? -1 : 1
                if (index >= Math.round(blocksNum.x / 2)) {
                    if (index == Math.round(blocksNum.x / 2)) {
                        xDistance = 0
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        xDistance = side * 35
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        xDistance = side * 70
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        xDistance = side * 105
                    } else if (index % Math.round(blocksNum.x / 2) == 4) {
                        xDistance = side * 140
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        xDistance = side * 175
                    }
                } else {
                    if (index % Math.round(blocksNum.x / 2) == 4) {
                        xDistance = side * 35
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        xDistance = side * 70
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        xDistance = side * 105
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        xDistance = side * 140
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        xDistance = side * 175
                    }
                }
                return `${xDistance}px`;
            },
        }, "<").to(canvases3, {
            y: (index, target) => {
                let yDistance;
                if (index >= Math.round(blocksNum.x / 2)) {
                    if (index == Math.round(blocksNum.x / 2)) {
                        yDistance = window.innerHeight * 1.5
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        yDistance = window.innerHeight * 1.3
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        yDistance = window.innerHeight * 1.2
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        yDistance = window.innerHeight * 1.1
                    } else if (index % Math.round(blocksNum.x / 2) == 4) {
                        yDistance = window.innerHeight * 1
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        yDistance = window.innerHeight * 0.9
                    }
                } else {
                    if (index % Math.round(blocksNum.x / 2) == 4) {
                        yDistance = window.innerHeight * 1.3
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        yDistance = window.innerHeight * 1.2
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        yDistance = window.innerHeight * 1.1
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        yDistance = window.innerHeight * 1
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        yDistance = window.innerHeight * 0.9
                    }
                }
                return `-${yDistance}px`;
            },
            duration: 1
        }, "<").fromTo(canvases3, {
            x: 0
        }, {
            x: (index, target) => {
                let xDistance;
                let side = index < Math.round(blocksNum.x / 2) ? -1 : 1
                if (index >= Math.round(blocksNum.x / 2)) {
                    if (index == Math.round(blocksNum.x / 2)) {
                        xDistance = 0
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        xDistance = side * window.innerWidth / 35
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        xDistance = side * window.innerWidth / 35 * 2
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        xDistance = side * window.innerWidth / 35 * 3
                    } else if (index % Math.round(blocksNum.x / 2) == 4) {
                        xDistance = side * window.innerWidth / 35 * 4
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        xDistance = side * window.innerWidth / 35 * 5
                    }
                } else {
                    if (index % Math.round(blocksNum.x / 2) == 4) {
                        xDistance = side * window.innerWidth / 35
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        xDistance = side * window.innerWidth / 35 * 2
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        xDistance = side * window.innerWidth / 35 * 3
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        xDistance = side * window.innerWidth / 35 * 4
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        xDistance = side * window.innerWidth / 35 * 5
                    }
                }

                return `${xDistance}px`;
            },
        }, "<").to(canvases4, {
            y: (index, target) => {
                let yDistance;
                if (index >= Math.round(blocksNum.x / 2)) {
                    if (index == Math.round(blocksNum.x / 2)) {
                        yDistance = window.innerHeight * 1.3
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        yDistance = window.innerHeight * 1.2
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        yDistance = window.innerHeight * 1.1
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        yDistance = window.innerHeight * 0.9
                    } else if (index % Math.round(blocksNum.x / 2) == 4) {
                        yDistance = window.innerHeight * 0.6
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        yDistance = window.innerHeight * 0.4
                    }
                } else {
                    if (index % Math.round(blocksNum.x / 2) == 4) {
                        yDistance = window.innerHeight * 1.4
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        yDistance = window.innerHeight * 1.2
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        yDistance = window.innerHeight * 0.9
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        yDistance = window.innerHeight * 0.6
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        yDistance = window.innerHeight * 0.4
                    }
                }

                return `-${yDistance}px`;
            },
            duration: 1
        }, "<").fromTo(canvases4, {
            x: 0
        }, {
            x: (index, target) => {
                let xDistance;
                let side = index < Math.round(blocksNum.x / 2) ? -1 : 1
                if (index >= Math.round(blocksNum.x / 2)) {
                    if (index == Math.round(blocksNum.x / 2)) {
                        xDistance = 0
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        xDistance = side * window.innerWidth / 30
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        xDistance = side * window.innerWidth / 30 * 2
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        xDistance = side * window.innerWidth / 30 * 3
                    } else if (index % Math.round(blocksNum.x / 2) == 4) {
                        xDistance = side * window.innerWidth / 30 * 4
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        xDistance = side * window.innerWidth / 30 * 5
                    }
                } else {
                    if (index % Math.round(blocksNum.x / 2) == 4) {
                        xDistance = side * window.innerWidth / 30
                    } else if (index % Math.round(blocksNum.x / 2) == 3) {
                        xDistance = side * window.innerWidth / 30 * 2
                    } else if (index % Math.round(blocksNum.x / 2) == 2) {
                        xDistance = side * window.innerWidth / 30 * 3
                    } else if (index % Math.round(blocksNum.x / 2) == 1) {
                        xDistance = side * window.innerWidth / 30 * 4
                    } else if (index % Math.round(blocksNum.x / 2) == 0) {
                        xDistance = side * window.innerWidth / 30 * 5
                    }
                }

                return `${xDistance}px`;
            },
        }, "<").from(".second-page", {
            duration: 0.6,
            opacity: 0,
        }, "<")
    }

 

Link to comment
Share on other sites

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

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

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

It looks like you put ignoreMobileResize: true in your ScrollTrigger object on that tween - that is incorrect and won't do anything at all. Please follow the directions from the original response: 

ScrollTrigger.config({ ignoreMobileResize: true });

If you still need help, please post a minimal demo

 

If you're seeing performance problems those often come down to how browsers and graphics rendering work. It's very difficult to troubleshoot blind and performance is a DEEP topic, but here are some tips: 

  1. Try setting will-change: transform on the CSS of your moving elements. 
  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!  💚

Link to comment
Share on other sites

See the Pen bGjBRLv by yoz0713 (@yoz0713) on CodePen


Here is the link to my CodePen that demonstrates the problem I encountered. Due to CORS restrictions on the canvas API, I cannot use images on CodePen. Therefore, I used CSS to style the canvas and set the canvas element to black (it should have rendered a specific area of an image).

You can observe that the animation becomes laggy when the menu bar is toggled to show or close by using an iPhone to watch this demo.
thank you.🥰

Link to comment
Share on other sites

Hi,

 

That's a lot of code (over 700 lines). Also it caughts my attention that you have a bunch of different <canvas> tags. Why not pu everything inside a single canvas element? Right now you're animating a bunch of different HTML Nodes. You'll get far better performance animating everything inside canvas.

 

This is an example I made a looong time ago. It uses PIXI to create a proof of concept that emulates the Jetlag site:

https://jetlag.photos/

 

See the Pen fd2f9d0937490197a87f18210d71ac64 by rhernando (@rhernando) on CodePen

 

Even in a mid-range android phone works without any issues.

 

Finally I don't know what you mean with menu bar here:

12 hours ago, Danile Yu said:

You can observe that the animation becomes laggy when the menu bar is toggled to show or close by using an iPhone to watch this demo.

Are you referring to the browser's url address bar? If so you should try using ScrollTrigger.normalizeScroll():

https://greensock.com/docs/v3/Plugins/ScrollTrigger/static.normalizeScroll()

 

Hopefully this helps.

Happy Tweening!

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