79501130

Date: 2025-03-11 14:42:20
Score: 2
Natty:
Report link

I have decided to share the answer I've come up with on my own in case someone else after me has the same question and hopes to find an answer here:

    document.querySelector("body").onscroll = function() {
    const main             = document.querySelector("main");
    const backgroundHeight = main.offsetWidth * (3.0/2.0);
    const screenHeight     = Math.max(window.screen.availHeight, window.innerHeight);
    const mainHeight       = main.offsetHeight;
    
    const factor = 1.0 - ((backgroundHeight - screenHeight) / (mainHeight - screenHeight));
    const yvalue = - main.getBoundingClientRect().top * factor;
    const xvalue = "center";
    main.style.backgroundPosition = xvalue + " " + yvalue + "px";
    }

const main = document.querySelector("main");

I do this because the background image I want the parallax effect to work on applies to the main element.

const backgroundHeight = main.offsetWidth * (3.0/2.0);

The formula I came up with to always align the background image's bottom with the page's bottom requires the height of the background image. For that I use main.offsetWidth (I set main to take up 100% of the width of the page so this works) multiplied with the height/width ratio of the background image. In the case of the parrot image I used as example, it's 3/2.

screenHeight = Math.max(window.screen.availHeight, window.innerHeight);

One also needs the height of the screen. The problem is that I cannot use a single value as sometimes it gives me a too small result. However, using the maximum of either window.screen.availHeight or window.innerHeight always seems to work to me.

mainHeight = const mainHeight = main.offsetHeight;

And one needs the height of the element you want to apply the parallax effect on.

const factor = 1.0 - ((backgroundHeight - screenHeight) / (mainHeight - screenHeight));

This is the formula I came up with thanks to a geometric sketch.

const yvalue = - main.getBoundingClientRect().top * factor;

I found out that "- main.getBoundingClientRect().top" seems to work better than "const scrolltotop = document.scrollingElement.scrollTop" I used before. main.getBoundingClientRect().top basically returns the distance from the top of the main element to the top of the screen and becomes a negative value once you have scrolled past main's top. This is why I added a minus but Math.abs() works too.

const xvalue = "center"; main.style.backgroundPosition = xvalue + " " + yvalue + "px";

Here you just insert the values into the background.

Right now this function is executed everytime you scroll. main, backgroundHeight, screenHeight and mainHeight stay constant while just scrolling so it would make sense to initialise those values in a separate function executed on load or on resize but not on scroll - unless main changes its size automatically or upon user interaction. I also learnt first-hand while testing that Chrome has massive performance issues with repositioning background images larger than 1000x1000 so please keep this in mind.

Reasons:
  • Blacklisted phrase (0.5): thanks
  • Blacklisted phrase (0.5): I cannot
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (1):
Posted by: Lily