0

I have a div and when I drag my finger up I want a number inside the div to increase.

When I drag my finger down I want the number inside to decrease.

This is fairly simple to implement with a touch event.

The codesandbox is here - I tried to make it as simple as possible! Codesandbox

Where I have a problem is the event fires really quickly, and this makes it difficult to land on a particular number. It would be great to throttle the event using a lodash throttle function, but here I get into problems! Nothing happens!

I tried using useRef like so:

const throttledPosition = useRef(throttle(touchPosition => touchPosition, 200)).current;

Again, nothing happens.

Davtho1983
  • 3,827
  • 8
  • 54
  • 105

1 Answers1

1

Part 1 - Throttling Event Handler

Your first issue is caused by the way you are using _.throttle(), it should ideally be wrapped around your event handler callback.

Please review the examples in lodash.throttle() docs for reference on usage.

Also have a look at this SO question it may offer you some more insight into using throttle within React Hooks.

The key change was to wrap the event callback with throttle.

  const throttledSetPosition = throttle(event => {
    if (event.touches[0].clientY) {
      if (slider.current.contains(event.target)) {
        setTouchPosition(Math.round(event.touches[0].clientY));
      }
    } else {
      setTouchPosition(null);
    }
  }, 200);

  const handleTouchMove = useCallback(throttledSetPosition, [touchPosition]);

Part 2 - Increasing/Decreasing Value

To achieve your goal of increasing/decreasing the displayed value, you first need to identify a scale. What is the max-min you want displayed? Let's use 100 as that is easily understood.

What you then need to calculate is the percentage of 100 the user is currently touching at, but reversed (since closer to the top is really closer to 100 and further down is closer to 0).

To do this you could use the following:

// Define the scale
const scale = 100;

// Extract needed variables from event touches array
const {
  clientY,
  target: { offsetHeight, offsetTop }
} = event.touches[0];

// Calculate the percentage
const percentage = (clientY - offsetTop) / offsetHeight;

// Make the calculation to be reversed by subtracting the percentage from the scale...
const currentValue = scale - (percentage * scale);

// Set the display value
setTouchPosition(Math.round(currentValue));

I have forked your sandbox here with the above alterations.

segFault
  • 3,887
  • 1
  • 19
  • 31
  • 1
    Thanks for giving it some thought! I tried playing around with the delay and it doesn't seem to delay between each callback? – Davtho1983 Mar 30 '20 at 12:44
  • 1
    I just updated my answer and sandbox with an altered version following the answers given in the SO question I also recently added. Sorry for the many edits! let me know if it still isn't behaving as expected. – segFault Mar 30 '20 at 12:47
  • 1
    Ok this is behaving a lot better, but the thing that confuses me is that, if I want the number to increment by 1 if event.touches[0].clientY is +ve and I change setTouchPosition(Math.round(event.touches[0].clientY)); to setTouchPosition(touchPosition + 1); - it doesn't work anymore – Davtho1983 Mar 30 '20 at 13:05
  • 1
    updated my answer and sandbox to calculate the displayed value based on a "scale", in the example I use 100, but you can use whatever scale you want, it should work the same. – segFault Mar 30 '20 at 13:35