score:2

Accepted answer
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>
  );
}

score:2

    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;

Related Query

More Query from same tag