Jump to content
Search Community

ScrollTrigger.refresh() works incorrect on Mobile/iPhone

DanielLav test
Moderator Tag

Recommended Posts

Hello GSAP community! 👋 I'm using the vanilla-lazyload library to lazy load images.

 

I need to use lazy loading because I'm working on a project page and there are a lot of photos/videos that weigh a lot.

 

The whole problem lies in the ScrollTrigger position after the images, which are loaded lazily. The fact is that initially these images do not have a height and because of this ScrollTrigger fires earlier than necessary (which is correct).

 

To solve this problem I used the method when loading each image

ScrollTrigger.refresh()

This method allows ScrollTrigger to work when needed, but on phones (I tested on iPhone 14 Pro in Safari and Google Chrome) when scrolling, the scroll stops at the moment this method is executed.

 

 

 

How to fix the problem of stopping when scrolling quickly on mobile devices?

 

 

 

I tried to solve this using the method

ScrollTrigger.normalizeScroll(true)

But this method removes smooth scrolling on mobile devices, which also doesn’t look very good.

 

I also use the lenis-scroll library, but if I turn it off the problem remains.

 

I am attaching my codepen so you can understand the reason:

(Be sure to watch it on your phone to understand why).

 

I hope there is a very easy solution to my issue. But I've already spent several evenings trying to find something worthwhile.

 

I will be very grateful for your help and believe in the magic of GSAP 💫

See the Pen QWRVYYz by ProjectDCL (@ProjectDCL) on CodePen

Link to comment
Share on other sites

Hello @DanielLav,

So you said your images at the start did not have height. I'm not sure if this is important. Are you considering an approach in which you would initially set the dimensions? This approach also works well if inspected with iPhone 14 in the inspector. So now the images of parents have fixed sizes, and then the images are loading with lazy load in their fixed parents. So you don't need to recalculate page size when they are loaded with ScrollTrigger.refresh().

The demo: 

See the Pen zYQXaYB by Hideakimaru (@Hideakimaru) on CodePen



Hope this could help in this issue.
Thanks, 
Hideakimaru 

Link to comment
Share on other sites

57 minutes ago, Hideakimaru said:

Привет@DanielLavИтак ,

вы сказали, что ваши изображения вначале не имели высоты. Я не уверен, важно ли это. Рассматриваете ли вы подход, при котором вы изначально задали бы размеры? Этот подход также хорошо работает при проверке с помощью инспектора iPhone 14. Итак, теперь изображения родителей имеют фиксированные размеры, а затем изображения загружаются с отложенной загрузкой в своих фиксированных родителях. Поэтому вам не нужно пересчитывать размер страниц при их загрузке с помощью ScrollTrigger.refresh().

Демо: 

 


Надеюсь, это может помочь в этом вопросе.
Спасибо, 
Хидеакимару 

 

 

Have you ever published project on Behance?

 

When designers create cases for Behance, they have no restrictions on the height or proportion of images.

And specifically in this case, I need to implement the ability to simply take photos for the Behance project and insert them onto the site. That is, the height of the images may be different.

 

Moreover, I see the problem of “zero height for deferred images” and ScrollTrigger in many of my projects. Previously, I simply made do with fixed sizes for the height of the elements, but in this case this approach does not work. And I think it's a bit of a crutch. 😅

 

As I said earlier, ScrollTrigger.refresh() solve problem with ScrollTriggers positions, but stop scroll on mobile devices when refresh. 

 

Maybe you have other ideas how to solve this problem? I will be really helpful for any info ☺️ 

Link to comment
Share on other sites

Hi @DanielLav,

So, now I understand what are you trying to do. You are refreshing the ScrollTrigger each time when the element is done with load. So looks like everything works correctly in your approach. But I really can't find this issue. Because if open your pen with iPhone 14 in the inspector. Everything is the same with the desktop version. The behavior is the same. Maybe you could describe step by step what you do to find this problem?
Example:

  1. Open code inspector with the iPhone 14 sizes.
  2. Scroll quickly to the footer section.
  3. You will see issues
  4. etc.

This could help to find your problem faster. Also, do you try to watch the same issue in other browsers, devices, etc? Did you find something else? Because with your version desktop and mobile behaviors are the same for me.

Thank you so much!
Hideakimaru

Link to comment
Share on other sites

As far as I know there isn't really a solution here beyond setting a height on the images.

ScrollTrigger needs the triggers to be refreshed if the height of the page changes, the only way to do this is to call ScrollTrigger.refresh().

You can deal with the scrolling issue on mobile by either

1) Avoiding calling refresh entirely by making sure the height doesn't change (height on images)
2) OR as you mentioned, using smooth scrolling in order to keep everything on the same thread.

This isn't ScrollTrigger deliberately halting the scroll, this is just how mobile safari deals with the additional JS load and layout calculations that are happening.

All of these decisions come with tradeoffs, it's just a case of deciding which things are the most important/possible for your use case.


Basically you can't have it all  -

 

- no animation + lazy loading

- animation + lazy loading with a height on the images

- animation + lazy loading with no height + smooth scroll
- animation + no lazy loading + no height on the images

I would love to be proved wrong here, so if anyone has a magic solution feel free to jump in. 

  • Like 2
Link to comment
Share on other sites

I also encountered this problem, but I used it in react, but I used a timer to make requests. I don’t think I have solved this problem very well. I really hope there are other methods. Remember to call me in time.

 

		const timer = setTimeout(() => {
			ScrollTrigger.refresh();
		}, 2000);
		// 清除定时器
		return () => {
			clearTimeout(timer);
		};

 

Link to comment
Share on other sites

13 minutes ago, cupid said:

I also encountered this problem, but I used it in react, but I used a timer to make requests. I don’t think I have solved this problem very well. I really hope there are other methods. Remember to call me in time.

 

		const timer = setTimeout(() => {
			ScrollTrigger.refresh();
		}, 2000);
		// 清除定时器
		return () => {
			clearTimeout(timer);
		};

 


Hi @cupid,

In this case, you don't know how fast your images will load. It will depend on many factors. For example, go to the Network tab in the dev tools menu and set the slow 3G in the network throttling menu. You will understand why you should avoid this approach. You can't rely on all images to load after 2 seconds. You should either avoid this approach or tie this method to more objective things.

Thank you so much!
Hideakimaru

  • Like 2
Link to comment
Share on other sites

Yeah, 100% this is not the correct way to handle this. ScrollTrigger.refresh() shouldn't be called continuously like this. It's quite expensive, only call it when the triggers definitely need to be recalculated.

Link to comment
Share on other sites

For the record, there's a lazy loading helper function here: 

And here's an enhanced version that allows you to set soft: true so that it'll only fire the ScrollTrigger.refresh() after the current scrolling behavior is finished: 

function handleLazyLoad(config={}) {
	let lazyImages = gsap.utils.toArray("img[loading='lazy']"),
		timeout = gsap.delayedCall(config.timeout || 1, ScrollTrigger.refresh).pause(),
		lazyMode = config.lazy !== false,
		imgLoaded = lazyImages.length,
		onScrollEnd = () => {
			ScrollTrigger.removeEventListener("scrollEnd", onScrollEnd);
			ScrollTrigger.refresh();
		},
		softRefresh = () => config.soft && ScrollTrigger.isScrolling() ? ScrollTrigger.addEventListener("scrollEnd", onScrollEnd) : ScrollTrigger.refresh(),
		onImgLoad = () => lazyMode ? timeout.restart(true) : --imgLoaded || softRefresh();
	lazyImages.forEach((img, i) => {
		lazyMode || (img.loading = "eager");
		img.naturalWidth ? onImgLoad() : img.addEventListener("load", onImgLoad);
	});
}

Usage:

handleLazyLoad({ 
  soft: true,  // wait until any current in-progress scrolling is done before calling ScrollTrigger.refresh()
  lazy: false, // changes all images to load="eager".
  timeout: 1   
});

The idea is to check ScrollTrigger.isScrolling() and if it's true, set up a "scrollEnd" event listener in which you can call ScrollTrigger.refresh(). 

 

I hope that helps.

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