Madhava Yedla
Madhava Yedla

Reputation: 1

How to change image src on scroll in reactjs

I want to change the source of image onscroll in reactjs. Like if scrollY is greater than 100 change the image source and if it is greater than 200 change it another source. i tried to do it but could not. any ideas?

    import React, { useEffect, useState, useRef } from 'react';
import './Video.css';
import { useInView } from 'react-intersection-observer';

function Video() {

    const videoSrc1 = "https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/6341303c29c5340961dc9ae6_Mco-1-transcode.mp4";

    const videoSrc2 = "https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/63413ff244f1dc616b7148a0_Mco-transcode.mp4";
        const videoSrc3 = "https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/63455a67996ba248148c4e31_add-options%20(3)-transcode.mp4";
    
    const img1 = 'https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/63455a67996ba248148c4e31_add-options%20(3)-poster-00001.jpg';
    const img2 = 'https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/63413ff244f1dc616b7148a0_Mco-poster-00001.jpg';
    const img3 = 'https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/63455a67996ba248148c4e31_add-options%20(3)-poster-00001.jpg';


    const [scrollPosition, setScrollPosition] = useState(0);
    const handleScroll = () => {
        const position = window.pageYOffset;
        setScrollPosition(position);
    };

    useEffect(() => {
        window.addEventListener('scroll', handleScroll, { passive: true })

        return () => {
            window.removeEventListener('scroll', handleScroll);
        };
    }, []);


    {
        if (scrollPosition>=316){
            // this.src={videoSrc2}
        }
    }
   console.log("position;", scrollPosition)


    return (
        <div className='container'>

            <video loop autoPlay muted className='video'>
                <source src={videoSrc1} type="video/webm" />
            </video>

           
   

        </div>
    )
   
}

export default Video

Upvotes: -1

Views: 2227

Answers (2)

kockburn
kockburn

Reputation: 17636

Setting the scroll position will trigger needless rerenders, instead you only want to trigger a rerender when the data source will change.

To select the proper data source, putting the list of data sources in a list is a good way to do this. Then you can properly determine the index of data source to show with something like this:

// Y_OFFSET_DIFFERENCE is the value that determines when the next image should be shown.
const index =
        Math.floor(position / Y_OFFSET_DIFFERENCE) % dataSources.length;

You can see how this is properly calculated:

  1. If position = 0 and Y_OFFSET_DIFFERENCE = 100 then 0/100 = 0 and 0 % 2 is 0. 0 is the index of the first element of your list.
  2. If position = 100 and Y_OFFSET_DIFFERENCE = 100 then 100/100 = 1 and 1 % 2 is 1. 1 is the index of the second element in your list.
  3. If position = 150 and Y_OFFSET_DIFFERENCE = 100 then 150/100 = 1.5 and Math.floor(1.5) = 1 and 1 % 2 is 1. 1 is the index of the second element in your list.
  4. If position = 200 and Y_OFFSET_DIFFERENCE = 100 then 200/100 = 2 and 2 % 2 is 0. 0 is the index of the first element in your list.

And it'll continue like this forever.

Here is the full code.

import { useState, useEffect } from "react";

const dataSources = [
  "https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/63455a67996ba248148c4e31_add-options%20(3)-poster-00001.jpg",
  "https://global-uploads.webflow.com/62efc7cb58ad153bfb146988/63413ff244f1dc616b7148a0_Mco-poster-00001.jpg"
];

const DEFAULT_DATA_SOURCE = dataSources[0];
const Y_OFFSET_DIFFERENCE = 100;

export default function App() {
  const [dataSource, setDataSource] = useState(DEFAULT_DATA_SOURCE);

  useEffect(() => {
    const handleScroll = () => {
      const position = window.pageYOffset;
      const index =
        Math.floor(position / Y_OFFSET_DIFFERENCE) % dataSources.length;
      const selectedSource = dataSources[index];
      if (selectedSource === dataSource) {
        return;
      }
      setDataSource(selectedSource);
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [dataSource]);

  return (
    <div
      style={{ height: 2000, backgroundImage: "linear-gradient(blue, green)" }}
    >
      <div
        style={{
          position: "sticky",
          top: 10,
          left: 10,
          display: "flex",
          justifyContent: "center",
          flexDirection: "column",
          alignItems: "center"
        }}
      >
        <p style={{ color: "white", textAlign: "center" }}>{dataSource}</p>
        <img
          src={dataSource}
          alt="currently selected source"
          width={100}
          height={100}
        />
      </div>
    </div>
  );
}

codesandbox demo

Upvotes: 0

ls170292
ls170292

Reputation: 100

You can do a combination of Vanilla JS methods and React methods to achieve this.

Your best best bet is to use the useEffect hook and add an event listener to the Window DOM object based on where on the page the scroll is position.

  • First you need a function that executes every time the DOM re-renders (scrolling does this)

start by using the useEffect hook

useEffect(() => {}, [])
  • Next you want a function that executes specifically when you scroll the page

you can add an event handler to the window DOM element

window.addEventListener('scroll',() => {})
  • Then you want to track the where you are on the page (how far up or how far down)

You can use the window's scrollTop property to return how far up or down you are on the page relative to the top of the page

document.documentElement.scrollTop 
  • Now comes the logic part, you said you want to change the image's src based on how far up or down you've scrolled on the page

This is where, useState, boolean flags and the ternary operator come into play

  • You can write a useState hook to store the Y position of the scroll, and the useEffect and scroll event listener will keep updating it to the current position
const [scrollPosition, getScrollPositon] = useState(document.documentElement.scrollTop)

  • finally nest the hook function into the window 'scroll' function and nest that in the useEffect hook
const [scrollPosition, getScrollPositon] = useState(document.documentElement.scrollTop)

useEffect(() => {
  window.addEventListener('scroll',() => {
    getScrollPositon(document.documentElement.scrollTop);
  })
}, [])

AND finally write the logic in your .jsx code to say 'when we are x number of pixel below the top of the screen...change the image source'


const App = () => {

  return (
    <div className='app'>
      <img src={scrollPosition < 1000 ? 'http://imagelinkA.com' : 'http://imagelinkB.com'}>
    </div>
  ); 

}

Now you put it all together...

// App.js/jsx

import { useState, useEffect } from 'react';

const App = () => {

  // initial scroll positon on page load
  const [scrollPosition, getScrollPositon] = useState(document.documentElement.scrollTop)

  // hook and event handlers to keep track of and update scroll
  useEffect(() => {
    window.addEventListener('scroll',() => {
    getScrollPositon(document.documentElement.scrollTop);
    })
  }, [])

  // your .jsx code with appropriate boolean flags and use of the ternary operator
  return (
    <div className='app'>
      <img src={scrollPosition < 1000 ? 'http://imagelinkA.com' : 'http://imagelinkB.com'}>
    </div>
  ); 

}

Hope I was able to help!

Upvotes: 0

Related Questions