skip to Main Content

I wanted to use a scroll effect (parallax + filters) in a very simple page. It’s just a sticky 120vh div with a background image and some text.

Everything works fine in the desktop browser and with the mobile view of desktop browsers, however on my real device I experience a weird glitch.

If you scroll up and back down the panel gets stuck because the container shrinks and doesn’t scroll to the end anymore. This only happens when you scroll down and then back up.
I presume it’s due to the window changing size because of the scrollbar appearing.

When I don’t scroll manually, but instead click on the panel which does a scrollToTop / scrollToBottom everything works fine. But as soon as I manually scroll from the bottom back to the top the site breaks.

Here are three screenshots. I’m using a Pixel 5 and Chrome (on Android).

You can experience this live by visiting https://javascript-moe-git-preview-scroll-issue-c5h8nnao4s-projects.vercel.app/about on a similar device (I guess).

I deployed a "dirty workaround" on production which you can see at https://javascript.moe/about which simply finishes the animation earlier, but you can still experience the issue when scrolling down, back up and back down again. When you reload the page after it’s stuck, you can see it revert back to normal on page unload.

Here is the code that causes the problem. I tried to isolate it, but I no matter what I try removing, the bug keeps persisting until I remove the content entirely.

Here are some screenshots to illustrate the problem and the code:

enter image description here
enter image description here
enter image description here

import { getVH, scrollToTop } from "../lib/util"
import { StickySection, sectionCtx } from "../components/AnimatedSection"
import { BackgroundImage } from "../components/BackgroundImage"
import { Parallax } from "../components/anim/Parallax"
import {
    motion,
    useScroll, useTransform
} from 'framer-motion';
import ArrowBack from '../assets/arrowback.svg?react'
import { Link } from "react-router-dom"
import { useContext } from "react";
import { AboutSectionProps } from "@/lib/types";
import { ABOUT_TEXT } from "@/lib/const";

export const AboutPage = () => {
    return <>
        <StickySection height='120lvh' >
            <AboutSection text={ABOUT_TEXT} />
        </StickySection >
    </>
}

export const AboutSection = ({ text }: AboutSectionProps) => {
    const { ref: scrollRef } = useContext(sectionCtx);
    const { scrollYProgress } = useScroll({
        layoutEffect: true,
        target: scrollRef || undefined,
        offset: ["start start", "end end"]
    });

    const dist = getVH(50)
    const offset = -dist;
    const blur = useTransform(scrollYProgress, [0, 1], ['blur(4px)', 'blur(0px)'])
    const rblur = useTransform(scrollYProgress, [0, 1], ['brightness(100%) blur(0px) saturate(100%)', 'brightness(80%) blur(4px) saturate(140%)'])
    const background = useTransform(scrollYProgress, [0, 1], ['#FFFFFF11', '#00000033'])
    const overflowY = useTransform(scrollYProgress, [0, 1], ['hidden', 'auto']);

    return <>
        <BackgroundImage src="/images/wallpaper/5.webp" alt="Seepark in Freiburg" />
        <div className='w-[80ch] max-w-[calc(100vw-32px)] absolute top-0'>
            <Parallax distance={32 * 2} offset={32 * 1} className="flex"  >
                <Link to="/" className="flex">
                    <ArrowBack style={{ fill: 'white' }} />
                    <h2>Back</h2>
                </Link>
            </Parallax>
            <Parallax distance={dist - 32 * 4} offset={offset + 32 * 2}>
                <button
                    onClick={scrollToTop}
                >
                    <motion.div
                        style={{
                            background,
                            backdropFilter: rblur,
                            overflowY
                        }}
                        className="p-4 rounded-md shadow-lg shadow-black max-h-[calc(100svh-120px)]" >
                        <motion.p style={{ filter: blur, textShadow: '0px 0px 1px black' }}>{text}</motion.p>
                    </motion.div>
                </button>
            </Parallax>
            <Parallax
                distance={dist - 32 * 2} offset={offset + 32} className="w-full absolute top-0 ml-4"
            >
                <button
                    className="w-fit"
                    onClick={scrollToTop}
                >
                    <h1 style={{ textShadow: '0px 0px 3px black' }}>About Me</h1>
                </button>
            </Parallax>
        </div>
    </>
}

Once you scroll down and back up you can not scroll down again.
What is causing this issue and how can I fix it?

Edit: The issue seems to be caused by a combination of sticky, overflow: hidden, height: 100vh and a child elements overflowing the container.

A dirty workaround is to decrease the range of framers animation from [0, 1] to [0, 0.75] in order to let the animation unfold before the body reaches the bottom of the page.

Still no clue how to resolve this.

C5H8NNaO4/javascript.moe/blob/master/src/pages/AboutPage.tsx

2

Answers


  1. The problem was fixed after I changed the position and display of the <h1> tag.

    <h1 style={{ textShadow: '0px 0px 3px black', display:'inline',position:'relative' }}>About Me</h1>
    

    It seems this issue occurred because the <h1> tag was behaving like it was sticky, sticking to the top of its parent tag and preventing it from moving.

    enter image description here

    However, this isn’t normal behavior. When the text gets stuck, switching to another browser tab and then back fixes the issue.
    This means it may be a Chrome bug.

    Login or Signup to reply.
  2. approximately like so:

       // Scroll down by a specific amount incrementally
        const scrollDownMultipleTimes = (times: number, increment: number) => {
          cy.window().then((win) => {
            for (let i = 0; i < times; i++) {
              cy.wait(500); // Optional: Wait for any animations or content loading
              win.scrollBy(0, increment);
            }
          });
        };
        
        // Scroll down by 1000px increments, 3 times
        scrollDownMultipleTimes(3, 1000);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search