Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Increasing a value non-linearly when holding down a button (React)

I apologize if this has been asked before, but I haven't found anything to help me quite yet, and I am new to React!

I want a value to increase every n number of seconds a user holds down a button. Right now it is just increasing linearly ever 35 milliseconds!

So far I have something like this:

function Increaser() {

  const [value, setValue] = useState(0);
  const changeTimer: React.MutableRefObject<any> = useRef(null);

  function timeoutClearUp() {
    clearInterval(changeTimer.current);
  }

  function increment() {
    changeTimer.current = setInterval(() => setValue(prev => prev + 1), 35);
  };

  function onChange(e: any) {
    setValue(parseInt(e.target.value));
  }

  return (
    <div>
      <button onMouseUp={timeoutClearUp} onMouseDown={increment}>INCREASE</button>
      <input
            type="number"
            value={value}
            onChange={(e) => {onChange(e)}}
          />
    </div>
  );
}

I have tried adding another ref but it didn't seem to work! What is a small thing I can add to this code to ensure that every second value is incremented by a larger and larger value (the longer a user holds the button down for).

Thanks so much!

  • React: ^17.0.2
  • Typecript: ^4.5.5
like image 998
D00dood Avatar asked Nov 02 '25 12:11

D00dood


2 Answers

Because nobody gave you a real solution, here is one such way of doing what you want.
What I have done here is to use a refrence to the input, and have the value in your state updated once they stop clicking the increment button, or when they change the input manually.
In the increment function I use a setTimeout to create a custom interval, and this interval uses a math equation to determine what the next timeout/interval will be. The equation I used will go from 35ms (start) to 10ms (end) in 41 seconds and will not go below 10ms.
Here is a time chart of what I use in this example for speeding up the interval. (the x axis is time in seconds, and y axis is the delay in milliseconds) Graph of time function I used.

import { useState, useRef } from 'react';

function Increaser() {

    const [value, setValue] = useState(0);
    const myInputRef = useRef<HTMLInputElement>(null);
    let myTimeout: ReturnType<typeof setTimeout>;
    let startTime: Date;

    function stopInterval() {
        clearTimeout(myTimeout!);//Stop the interval in essence
        setValue(parseInt(myInputRef.current!.value!)||0) //Set the value of the input (in the mean time value is "floating")
    }
  
    function increment() {
      startTime = new Date();//Initialize when we started increasing the button
      myTimeout = setTimeout(function runIncrement() {//Start the timeout which is really a controlled interval
        const val = (parseInt(myInputRef!.current!.value)||0)+1;//Increment the value of the ref's target
        myInputRef!.current!.value = (val).toString()//Assign that incremented value to the ref
        const now = new Date();
        const delta = now.getTime() - startTime.getTime();
        const n = 10 + (1/(Math.pow(1.1,delta/1000-(2*Math.log(5))/Math.log(1.1))));//Whatever
        //The math function I have chosen above will go from 35 ms to 10 ms in 41 seconds.
        //The function will not go lower than 10 ms and starts at 35 ms
        //However you can use whatever function or math you want to get it to do as you wish.
        //...
        //whatever logic here you want to calculate when we will trigger the increment again.
        //...
        myTimeout = setTimeout(runIncrement, parseInt(n.toString()));//Increment again after the variable n milliseconds which we calculate above
      },35);//Start the interval with an initial time of 35 milliseconds
      //Now we know when the interval started, and we can
    };
  
    function onChange(e: any) {
      setValue(parseInt(e.target.value));
    }
  
    return (
      <div>
        <button onMouseUp={stopInterval} onMouseDown={increment}>INCREASE</button>
        <input
              type="number"
              onChange={(e) => {onChange(e)}}
              ref={myInputRef}
            />
      </div>
    );
  }
export default Increaser;

`

I appologize for the slight formatting that S.O. is doing here (in combination with my VS code editor).

like image 88
Sharp Dev Avatar answered Nov 05 '25 02:11

Sharp Dev


Thanks to SharpInnovativeTechnologies' answer, I realized how dumb I was and found an easy solution by going:

  function increment() {
    const past = Date.now()
    const rate = 500;
    changeTimer.current = setInterval(() => {
        const diff = Math.floor((Date.now() - past) / rate);
        setValue(prev => prev + (1 + diff))
      }, 50);
    }

Where rate is a sort of sensitivity adjustment.

So every 50ms The Date.now() - past increases and boosts the value with the setter.

like image 31
D00dood Avatar answered Nov 05 '25 01:11

D00dood



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!