Jump to content
Search Community

Flickering animation during scrolling

Cyprian test
Moderator Tag

Recommended Posts

I'm trying do example app and learn gsap and locomotive scroll. I create sample page, when during scrolling show some animation. In chrome browser all right, but when I'm trying in safari, my animation is flickering.

 

My code:

<template>
  <div class="scroll" data-scroll-container>
    <div class="horizontal-sections">
      <div class="pin-wrap">
        <div class="animation-wrap to-right">
          <div class="section">
            <NuxtLink to="/">
                <p> Chnage page</p>
                <h2>Scroll Down</h2>
            </NuxtLink>
          </div>
          <div class="section section-amin section-gray">
            <h2>You need to animate on scroll. Performing some action.</h2>
            <div class="block-anim"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="section section-lightblue">
      <h2>Some section 1</h2>
    </div>
    <div class="section">
      <h2>Some section 2</h2>
    </div>
    <div class="horizontal-sections">
      <div class="pin-wrap">
        <div class="animation-wrap to-right">
          <div class="section">
            <NuxtLink to="/">
                <p> Chnage page</p>
                <h2>Scroll Down</h2>
            </NuxtLink>
          </div>
          <div class="section">
            <h2>Some section 2</h2>
          </div>
          <div class="section section-amin section-gray">
            <h2>You need to animate on scroll. Performing some action.</h2>
            <div class="block-anim1"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="section">
      <h2>Some section 2</h2>
    </div>
  </div>
</template>

<script setup lang="ts">
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
import LocomotiveScroll from 'locomotive-scroll';
gsap.registerPlugin(ScrollTrigger);

let locoScroll: any = null;

onMounted(() => {
  locoScroll = new LocomotiveScroll({
    el: document.querySelector('.scroll') as HTMLElement,
    smooth: true,
  });
  pinType: document.querySelector(".scroll").style.transform ? "transform" : "fixed"
  locoScroll.on('scroll', ScrollTrigger.update);

  ScrollTrigger.scrollerProxy('.scroll', {
    scrollTop(value) {
      return arguments.length
        ? locoScroll?.scrollTo(value, { duration: 0, disableLerp: true })
        : locoScroll?.scroll.instance.scroll.y;
    },
  });

  const horizontalSections = gsap.utils.toArray('.horizontal-sections');

  horizontalSections.forEach((section: any, i) => {
    const thisPinWrap = section.querySelector('.pin-wrap');
    const thisAnimWrap = thisPinWrap.querySelector('.animation-wrap');

    const getToValue = () => -(thisAnimWrap.scrollWidth - window.innerWidth);

    ScrollTrigger.create({
      trigger: section,
      scroller: '.scroll',
      start: 'top top',
      end: () => '+=' + thisAnimWrap.scrollWidth,
      pin: thisPinWrap,
      scrub: true,
    });

    const fakeHorizontalAnim = gsap.fromTo(
      thisAnimWrap,
      {
        x: () => (thisAnimWrap.classList.contains('to-right') ? 0 : getToValue()),
      },
      {
        x: () => (thisAnimWrap.classList.contains('to-right') ? getToValue() : 0),
        ease: 'none',
        scrollTrigger: {
          trigger: section,
          start: 'top top',
          scroller: '.scroll',
          end: () => '+=' + (thisAnimWrap.scrollWidth - window.innerWidth),
          scrub: true,
        },
      }
    );

    const tl = gsap.timeline();
    tl.to('.block-anim', {
      duration: 1,
      scroller: '.scroll',
      ease: 'power3',
      clipPath: 'inset(25% 25% 25.01% 25.01% round 25vw 25vw 25.01vw 25.01vw)',
    });

    const tl1 = gsap.timeline();
    tl1.to('.block-anim1', {
      duration: 1,
      scroller: '.scroll',
      ease: 'power3',
      clipPath: 'inset(25% 25% 25.01% 25.01% round 25vw 25vw 25.01vw 25.01vw)',
    });

    ScrollTrigger.create({
      trigger: section,
      start: () => 'top top-=' + (thisAnimWrap.scrollWidth - window.innerWidth),
      end: () => '+=' + window.innerWidth,
      animation: tl1,
      scroller: '.scroll',
      scrub: true,
    });

    ScrollTrigger.create({
      trigger: section,
      start: () => 'top top-=' + (thisAnimWrap.scrollWidth - window.innerWidth),
      end: () => '+=' + window.innerWidth,
      animation: tl,
      scroller: '.scroll',
      scrub: true,
    });
  });

  ScrollTrigger.refresh();
});

onBeforeRouteLeave((to, from, next) => {
  ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
  locoScroll?.destroy();
  next();
});
</script>

<style lang="scss" scoped>
body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;
}

h2 {
  position: relative;
  z-index: 2;
  margin: 0;
}

.section {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  width: 100vw;
  flex: 0 0 100vw;
  background: lightgray;
}
.section-gray {
  background: gray;
}
.section-lightblue {
  background: lightblue;
}

.block-anim {
   will-change: transform;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: lightgoldenrodyellow;
}

.horizontal-sections {
  position: relative;
  overflow-x: hidden;
  .pin-wrap,
  .animation-wrap {
    display: flex;
    position: relative;
    z-index: 1;
    height: 100vh;
  }
}

.spacer {
  height: 50vh;
  width: 100vw;
}
</style>

And link to page: https://skandynawia-przystan.vercel.app/test.

Do yoy know how I can fixed this ?

Link to comment
Share on other sites

I don't see the issue on my end but have seen it before with Safari and FF, which might have to do with the browser having to continuously re-render fixed elements as you scroll (so whether you see it or not may also depend on hardware). This probably has to do with setting the pinned element to position fixed and it creating a new stacking context when it's getting to clip-path or will-change OR be an issue with that AND overflow. Try removing either of those to see if the issue doesn't happen anymore. One thing you could try is forcing GPU acceleration on the fixed elements with them (or nested elements) by adding a 3D transform via transform:translate3d(0,0,0).

  • Like 1
Link to comment
Share on other sites

@davi actually, I would suspect the opposite. When something gets set to position: fixed, it allows the browser to just lock it into place (separate from scrolling), so you would NOT get any jittering. However, in some scenarios the pinning must be done by applying counter-transforms (like if you scroll the page down 10px, the element gets translateY(-10px) to make it look as if it's pinned). The problem there is that most browsers perform scrolling on a separate thread that is NOT synchronized with the main JS thread, so the browser might do the scroll thread which draws that element 10px further down, and then the main thread executes and the translateY(-10px) gets applied and it jumps back up. SUPER annoying and Apple's team told us it's literally impossible to work around. There are a bunch of serious bugs in iOS Safari too related to scroll (some have existed for years). ScrollTrigger.normalizeScroll(true) was our best crack at working around it, but it's not perfect for every scenario.  

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