Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

181
Views
Continuous scroll animation while mouse down or button hovered in ReactJs

Some context:

I'm trying to achieve a similar scrolling effect as in Etsy's product image thumbnails carousel. When you hover over the top part of the div, it automatically scrolls until it reveals the last image and same occurs on the bottom part.

Here's a link to a random product where you can check the functionality.

I've been trying to achieve this with react so I decided to start with scroll on mouse down and stop scrolling on mouse up.

I found a great example of this here

Problem is that it's using jquery. I tried to convert it to vanilla js and use on my react app but had no success.

After some more research I ended up with a working solution but the animation is not smooth at all. I used a setInterval hook. Here's my code.

import { useRef, useState, useLayoutEffect, useEffect } from "react";
import styled from "styled-components";

const Wrapper = styled.div`
  margin: 5% 40%;
  .test {
    height: 300px;
    width: 100px;

    overflow: scroll;
    .inner {
      background-image: linear-gradient(red, blue);
      height: 800px;
      width: 100%;
    }
  }
`;

function useInterval(callback, delay) {
  const savedCallback = useRef(callback);

  useLayoutEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {

    if (!delay) {
      return;
    }

    const id = setInterval(() => savedCallback.current(), delay);

    return () => clearInterval(id);
  }, [delay]);
}

const Scroll = () => {
  const scrollRef = useRef();
  const [delay, setDelay] = useState(100);
  const [scrolling, setScrolling] = useState(false);

  useInterval(
    () => {
      console.log("Scrolling");
      scrollRef.current.scrollBy({ top: 10, behavior: "smooth" });
    },
    scrolling ? delay : null
  );

  return (
    <Wrapper>
      <div ref={scrollRef} className="test">
        <div className="inner"></div>
      </div>

      <button
        onMouseDown={() => setScrolling(true)}
        onMouseUp={() => setScrolling(false)}
      >
        HOLD DOWN TO SCROLL
      </button>
    </Wrapper>
  );
};

export default Scroll;

I would really appreciate some directions and if possible a quick example on how I can achieve a smooth continuous scrolling

about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

It looks like useRef and requestAnimationFrame together with scrollTop could make it.

Please check examples (Stackblitz - https://stackblitz.com/edit/react-cuf5cl?file=src%2FApp.js):

import React from "react";
import "./style.css";

import { useRef } from "react";
import styled from "styled-components";

const Wrapper = styled.div`
  margin: 5% 40%;
  .test {
    height: 300px;
    width: 100px;

    overflow: scroll;
    .inner {
      background-image: linear-gradient(red, blue);
      height: 800px;
      width: 100%;
    }
  }
`;


const Scroll = () => {
  const step = 10;
  const scrollRef = useRef();
  const isScrollRef = useRef();

  const setMove = (state) => isScrollRef.current = state;

  const move = () => {
    if (isScrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollTop + step;
      requestAnimationFrame(move);
    }    
  };

  return (
    <Wrapper>
      <div ref={scrollRef} className="test">
        <div className="inner"></div>
      </div>

      <button
        onMouseDown={() => { setMove(true); move();}}
        onMouseUp={() => setMove(false)}
      >
        HOLD DOWN TO SCROLL
      </button>
    </Wrapper>
  );
};

export default Scroll;

export default function App() {
  return (
    <div>
      <Scroll />
    </div>
  );
}

about 4 years ago · Juan Pablo Isaza Report

0

Here's a version with configurable speed, and different speed precision. Note that the more speed steps you will have, the more the scrolling will result not smooth (Because scrollTop cannot be increased by a decimal number).

    import { useRef, useState, useLayoutEffect, useEffect } from "react";
import styled from "styled-components";

const Wrapper = styled.div`
  margin: 5% 40%;
  .test {
    height: 300px;
    width: 100px;
    overflow: scroll;
    .inner {
      background-image: linear-gradient(red, blue);
      height: 800px;
      width: 100%;
    }
  }
`;
function useInterval(callback, active, speed, speedRange = 10) {
  const savedCallback = useRef(callback);
  const intervalIdRef = useRef(null);

  useLayoutEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    clearInterval(intervalIdRef.current);

    if (speedRange < 1) {
      console.error(`Speed range must be >= 1`);
      return;
    }

    if (!(speed >= 1 && speed <= speedRange) || speed < 1) {
      console.error(`Speed must be in range [1...${speedRange}]`);
      return;
    }

    if (!active) {
      return;
    }

    intervalIdRef.current = setInterval(
      () => savedCallback.current(),
      speedRange / speed
    );

    return () => clearInterval(intervalIdRef.current);
  }, [active, speed, speedRange]);
}

const Scroll = () => {
  const scrollRef = useRef();
  const [speed, setSpeed] = useState(1);

  //Default speedRange is 10 = 10 different speeds [1..10]
  // (The higher this number the less smooth are the low speeds,
  // but there will be more speeds to chose from)
  const [speedRange, setSpeedRange] = useState(10);
  const [scrolling, setScrolling] = useState(false);

  useInterval(
    () => {
      scrollRef.current.scrollTop += 1;
    },
    scrolling,
    speed,
    speedRange
  );

  return (
    <Wrapper>
      <div ref={scrollRef} className="test">
        <div className="inner"></div>
      </div>

      <button
        onMouseDown={() => setScrolling(true)}
        onMouseUp={() => setScrolling(false)}
      >
        HOLD DOWN TO SCROLL
      </button>
    </Wrapper>
  );
};

export default Scroll;

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!