Анимация дыхания реакции

#reactjs #typescript #animation

#reactjs #машинописный текст #Анимация

Вопрос:

Я пытаюсь создать приложение для дыхания, используя React / Typescript. Я не хочу использовать React Native.

У меня есть круги дыхания, настроенные как компоненты, и у меня есть каждый круг, настроенный со своей собственной анимацией, если она у него есть. (Я не предоставляю этот код, потому что это не проблема)

С чем у меня возникают проблемы, так это с тем, чтобы анимация выполнялась бесконечно, сохраняя задержку от setTimeout(). (Смотрите код ниже)

 import React, { useState, useEffect } from "react";
import "./App.css";
import {
  BreatheInCircle,
  BreatheHoldInCircle,
  BreatheOutCircle,
  BreatheHoldOutCircle,
} from "./BreatheCircle";

const circles = [
  <BreatheInCircle/>,
  <BreatheHoldInCircle />,
  <BreatheOutCircle />,
  <BreatheHoldOutCircle />,
];

const App = () => {

  const [circleArray] = useState(circles);
  const [displayElement, setDisplayElement] = useState(<div></div>);
  const [index, setIndex] = useState(0);
  
  useEffect(() => {
    setTimeout(() => {
      setDisplayElement(circleArray[index]);
      setIndex(index   1);
    }, 4000);
  });

  return (
    <div>
      {displayElement}
    </div>
  );
}

export default App;`enter code here`
  

Когда я запущу это сейчас, он запустит useEffect один раз для каждого компонента, а затем остановится. Мне нужно, чтобы анимация повторялась, пока я ее не остановлю.

Редактировать

Я смог решить проблему задержки, с которой я столкнулся, но мне все еще нужна помощь с циклом. Ниже приведен обновленный код с изменяющейся задержкой.

 const App = () => {

  const [circleArray] = useState(circles);
  const [displayElement, setDisplayElement] = useState(<div></div>);
  const [index, setIndex] = useState(0);

  const timeoutDelays = [0, 4000, 4000, 4000, 4000];

  const changeCircle = () => {
    setCircle(circles[index]);
    setIndex(index   1);
  };

  useEffect(() => {
    setTimeout(changeCircle, timeoutDelays[index]);
  }, [index]);
  

если setInterval все еще является решением, я хотел бы знать, как его реализовать. Прямо сейчас он запускается один раз, затем останавливается.

Ответ №1:

Почему вы пытаетесь использовать тайм-аут вместо интервала? Также отправьте некоторые данные из родительского компонента в дочерний компонент для обновления анимации, я думаю, это не важно.

Комментарии:

1. Когда я использовал setInterval, произошла вспышка между компонентами при рендеринге следующего, и когда анимация была завершена (и я не уверен, почему это произошло), она случайным образом мигала одним из компонентов. Казалось, что это сводило меня с ума. Я попытался использовать тайм-аут, просто чтобы посмотреть, что произойдет, и это сработало почти идеально.

2. если вы хотите использовать setTimeout, то после установки индекса снова вызовите функцию setTimeout. но это нехорошо. setTimout вызывается только один раз, но как только он завершится, вы сможете вызвать функцию

3. Можете ли вы показать мне пример? Я не понимаю, что вы имеете в виду.

Ответ №2:

Ниже приведен окончательный код, который я использовал для ее решения. Он использовал setInterval, мне просто нужно было сделать это правильно. Я поддержал ответ пользователя 8119020, поскольку они привели меня к правильному способу решения этой проблемы.

 const BreatheCircles: FC = () => {
  const [circle, setCircle] = useState(<div></div>);
  const [index, setIndex] = useState(0);

  const circles = [
    <BreatheInCircle />,
    <BreatheHoldInCircle />,
    <BreatheOutCircle />,
    <BreatheHoldOutCircle />,
  ];

  const changeCircle = () => {
    setCircle(circles[index]);
    setIndex(index   1);
    if(index >= 3) {
        setIndex(0);
    }
  };

  useEffect(() => {
    const interval = setInterval(changeCircle, 4000);
    return () => clearInterval(interval);
  }, [changeCircle]);

  return <>{circle}</>;
};
  

Существует задержка перед запуском анимации, но это не было проблемой. Еще раз спасибо пользователю 8119020 за их помощь.

Обновить**

Всем привет, я знаю, что это немного позже, но я нашел лучший способ сделать это, используя чистый CSS. Ниже приведен пример кода, который я использовал для создания одной и той же функции дыхания без повторного рендеринга миллион раз.

Я использовал MUI для создания стилей на месте.

 
const breatheCircles = makeStyles((theme: Theme) =>
  createStyles({
    breatheCircle: {
      borderRadius: '50%',
      width: '30vh',
      height: '30vh',
      animation: '$breathe 17400ms infinite',
    },
    '@keyframes breathe': {
      '0%': {
        transform: 'scale(.5)',
        backgroundColor: '#71A1C9',
      },
      '24.9%': {
        backgroundColor: '#71A1C9',
      },
      '25%': {
        transform: 'scale(1)',
      },
      '25.1%': {
        backgroundColor: '#FF9833',
      },
      '50%': {
        transform: 'scale(1)',
        backgroundColor: '#FF9833',
      },
      '50.1%': {
        backgroundColor: '#000000',
      },
      '75%': {
        transform: 'scale(.5)',
        backgroundColor: '#000000',
      },
      '75.1%': {
        backgroundColor: '#FF9833',
      },
      '100%': {
        transform: 'scale(.5)',
        backgroundColor: '#FF9833',
      },
    },
  })
);

const Breathe: FC = () => {
  const classes = useStyles();
  const circleStyles = breatheCircles();
  const gender = useSelector<State>((state) => state.gender);

return (
    <>
      <Header />
      <div className={classes.root}>//styled elsewhere
        <div className={classes.circles}>//styled elsewhere
          <div className={circleStyles.breatheCircle} />
          <Counter />
        </div>
        <div className={classes.buttonBar}>
          <ButtonBar />
        </div>
      </div>
    </>
  );
};

  

Я просто подумал, что сообщество хотело бы видеть более эффективный способ анимации этих кругов.

Спасибо!