Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

182
Visualizações
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 Respostas
Responde à pergunta

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda