DEV Community

Jay Vijay Modi
Jay Vijay Modi

Posted on

Smooth parallax effect not responding properly

Currently what I'm trying to achieve is this, where 1 image moves from left to right as the user scrolls down creating a parallax effect:

image

Normally with DOM I would use:

let image = document.getElementById("image");

window.addEventListener('scroll', function() {
  let value = window.scrollY;
  image.style.marginRight = value * 4 + 'px';
  image.style.marginTop = value * 1.5 + 'px';
}
Enter fullscreen mode Exit fullscreen mode

But currently I'm trying to replicate this effect in React using setState, but the image doesn't move along with mouse scroll wheel and the layout does not seem right:

CodeSandBox: https://codesandbox.io/s/modest-hill-2wuko?file=/src/App.tsx

Code:

const [leftScroll,setLeftScroll] = useState(false);
const [topScroll,setTopScroll] = useState(false);
const [textScroll, setTextScroll] = useState(false);

  useEffect(function onFirstMount() {
    const changeBackground = () => {
      let value =  window.scrollY;
      console.log(window.scrollY);

      if(value>0 && value<200){
        setLeftScroll(true)
        setTopScroll(true)
        setTextScroll(true);
      }else{
        setLeftScroll(false)
        setTopScroll(false)
        setTextScroll(false);
      }
    }

    window.addEventListener('scroll', changeBackground);

    return null;
  }, []);
  return (
    <div className="background">
     <div style={{backgroundColor:"#0E2043",padding:"50px",height:"1000px",display:"flex",justifyContent:"center"}}>
      <div style={{color:"#fff",textAlign:"center",fontSize:"100px",fontFamily:"Roboto", display:"flex",justifyContent:"center"}}>Vanuatu</div>
      <img className="image"
              style={{marginRight:leftScroll ? '300px' : '0px',transition:"2s",marginTop:topScroll ? "300px" : "0px"}}
              src="https://cdn.britannica.com/87/122087-050-1C269E8D/Cover-passport.jpg"
            />
       <div
          className="text"
          style={{
            marginTop: textScroll ? "300px" : "800px",
            transition: "4s",
            marginLeft: "120px"
          }}
        >
          Minimum Investment
        </div>
    </div>
    </div>
Enter fullscreen mode Exit fullscreen mode

So is there an easier way to do this or can I implement those DOM functions in my React project and still have a parallax like effect?

Top comments (1)

Collapse
 
mkralla11 profile image
Mike Khirallah

Hi Jay! It looks like your first parallax example without React is using a different approach then what you are attempting to do in your React example.

As pseudocode, example 1 is stating:

"on every single scroll event, set the right margin / top margin to a value computed from the current y scroll value".

Example 2 is stating:

"on every single scroll event, set state for left/top/text to true when the y scroll is between 0 and 200, else set to false," and then you are using css transitions to try to mimic the position of the given parallax elements.

In example 2, the problem is you are setting a boolean on every scroll event and thinking that the css transitions will somehow know the correct margin to be at even though the margin will be hardcoded to either 300px or 0px for the image and 300px or 800px for the div. The transition for the css will occur over a period of time that is in no way linked to the incremental position of your scroll position. Here is a way to visualize it in steps, as a timeline, with example values that represent what is happening:

at 0 sec - scrollY is 0 - state leftScroll is true - image left marg is 300px

at 1 sec - scrollY is 100 - state leftScroll is true - image left marg is 300px

at 3 sec - scrollY is 300 - state leftScroll is **false** - image left marg is **0px**  [Changed!]
Enter fullscreen mode Exit fullscreen mode

In the last case, the image will transition to 0px, it will not transition to a point that is relative to the 300px scroll Y value. The image will indeed move across the screen, but not relative to the actual Y value.

To fix this, it's as simple as taking your first example, moving it into your useEffect in the second example, and then setting actual state values for the margin values you want to update as the scroll value changes.

I've updated your codesandbox to included the necessary changes.

codesandbox.io/s/modest-hill-2wuko...

Enjoy!