1

const {useState, useEffect, useRef} = React;
const App = () => {
  const [pressed, setPressed] = useState(false);
  const [shoot, setShoot] = useState(false);
  const [seconds, setSeconds] = useState(0);
  useInterval(() => {
    // Your custom logic here
    pressed && seconds < 3 && setSeconds((prev)=> Number((prev+0.1).toFixed(1)));
  }, 100);
  
  useInterval(()=>{
    !pressed && seconds > 0 && setSeconds((prev)=>{
      if( Number((prev-0.5).toFixed(1)) < 0){
        return 0;
      }
      return Number((prev-0.5).toFixed(1))
    });
  }, 20)
  return (
    <div>
      <button
        onMouseDown={()=>{
          console.log('mouseDown')
          setShoot(false);
          setPressed(true);
        }}
        onMouseUp={()=>{
          console.log('mouseUp')
          setShoot(true);
          setPressed(false);
        }}
        style={{
          transform: `rotate(-${seconds*15}deg)`
        }}
      >Press</button>
      <span className={`dot ${shoot ? '--shooted' : ''}`} />
      <p>{seconds}</p>
    </div>
  )
};
ReactDOM.render(<App />, document.getElementById('root'));

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

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}
.dot{
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius:100%;
  background: red;
}

.dot.--shooted{
  animation: test 1s;
}

@keyframes test{
  0%{
    transform: translateX(0px);
  }
  100%{
    transform: translateX(200px); // it should be dynamic px.
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.26.0/moment.min.js"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root" />

I'd like to move the red dot as much as the seconds I pressed the button.

but I am using animation so I can't control the px in CSS.

If I pressed the button for 3seconds, the red dot should be moved to 300px.

If I pressed the button for 1seconds, the red dot should be moved to 100px.

2
  • Just you ref and animate it with JS. Commented Jun 25, 2020 at 7:38
  • @demkovych could you give me some example? Commented Jun 25, 2020 at 7:40

1 Answer 1

2

This is an example. But you need to add a logic to move it back.

const {
  useState,
  useEffect,
  useRef
} = React;
const App = () => {
  const [pressed, setPressed] = useState(false);
  const [shoot, setShoot] = useState(false);
  const [seconds, setSeconds] = useState(0);
  const dotRef = useRef();

  useInterval(() => {
    // Your custom logic here
    pressed && seconds < 3 && setSeconds((prev) => Number((prev + 0.1).toFixed(1)));
  }, 100);

  useInterval(() => {
    !pressed && seconds > 0 && setSeconds((prev) => {
      if (Number((prev - 0.5).toFixed(1)) < 0) {
        return 0;
      }
      return Number((prev - 0.5).toFixed(1))
    });
  }, 20)

  const handleMouseUp = () => {
    dotRef.current.style.transform = `translateX(${seconds * 100}px)`;
  }

  return ( <
    div >
    <
    button onMouseDown = {
      () => {
        console.log('mouseDown')
        setShoot(false);
        setPressed(true);
      }
    }
    onMouseUp = {
      () => {
        console.log('mouseUp')
        setShoot(true);
        setPressed(false);
        handleMouseUp();
      }
    }
    style = {
      {
        transform: `rotate(-${seconds*15}deg)`
      }
    } >
    Press < /button> <
    span className = {
      `dot ${shoot ? '--shooted' : ''}`
    }
    ref = {
      dotRef
    }
    /> <
    p > {
      seconds
    } < /p> < /
    div >
  )
};
ReactDOM.render( < App / > , document.getElementById('root'));

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

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}
.dot {
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 100%;
  background: red;
  transition: transform 1s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.26.0/moment.min.js"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root" />

Sign up to request clarification or add additional context in comments.

1 Comment

To reset the ball after the animation you can add useEffect(() => { dotRef.current.addEventListener('webkitTransitionEnd', () => { dotRef.current.style.transform = '';});}, []) or add setTimeout(() => dotRef.current.style.transform = '', seconds * 1000) in handleMouseUp

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.