реализация бесконечного цикла изменения текста с помощью React Hooks - PullRequest
0 голосов
/ 03 октября 2019

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

const cdRotateIn = keyframes`
  0% {
    transform: rotateX(180deg);
    opacity: 0;
  }
  35% {
    transform: rotateX(120deg);
    opacity: 0;
  }
  65% {
    opacity: 0;
  }
  100% {
    transform: rotateX(360deg);
    opacity: 1;
  }
`

const cdRotateOut = keyframes`
  0% {
    transform: rotateX(0deg);
    opacity: 1;
  }
  35% {
    transform: rotateX(-40deg);
    opacity: 1;
  }
  65% {
    opacity: 0;
  }
  100% {
    transform: rotateX(180deg);
    opacity: 0;
  }
`

const Wrapper = styled.div``

const Headline = styled.h1``

const StaticText = styled.span``

const AnimatedTextContainer = styled.span`
  display: inline-block;
  perspective: 300px;
`

const AnimatedText = styled.b`
  opacity: 0;
  transform-origin: 50% 100%;
  transform: rotateX(180deg);
  display: inline-block;
  position: absolute;
  left: 0;
  top: 0;

  ${ifProp('isVisible', css`
    position: relative;
    opacity: 1;
    transform: rotateX(0deg);
    animation: ${cdRotateIn} 1.2s;
  `)}

  ${ifProp('isHidden', css`
    transform: rotateX(180deg);
    animation: ${cdRotateOut} 1.2s;
  `)}
`

const FlipAnimation = ({ words }) => {
  const [currentIndex, setCurrentIndex] = useState(0)
  const animationDelay = 1000

  useEffect(() => {
    let loopInterval = setInterval(() => {
      animateHeadline(words)
    }, animationDelay)
    return () => {
      clearInterval(loopInterval)
    }
  }, [])


  const animateHeadline = (words) => {
    setInterval(() => {
      setNextIndex()
    }, animationDelay);
  }

  const setNextIndex = () => {
    if (currentIndex < words.length - 1) {
      setCurrentIndex(currentIndex + 1)
    } else {
      setCurrentIndex(0)
    }
  }

  const animatedTexts = () => words.map((word, index) =>
    <AnimatedText key={index} isVisible={currentIndex === index} isHidden={currentIndex !== index}>{word}</AnimatedText>
  )

  return (
    <Wrapper>
      <Headline>
        <StaticText>My favourite food is</StaticText>
        <AnimatedTextContainer>
          {animatedTexts()}
        </AnimatedTextContainer>
      </Headline>
    </Wrapper>
  )
}
FlipAnimation.defaultProps = {
  words: ['bacon', 'eggs', 'sausage']
}

Проблема, которую я вижу здесь в консоли отладки, заключается в том, что currentIndex не обновляется должным образом при каждом запуске цикла. ,Я не могу понять, почему это происходит, так как этот код запускается каждую секунду:

if (currentIndex < words.length - 1) {
   setCurrentIndex(currentIndex + 1)
} else {
   setCurrentIndex(0)
}

1 Ответ

1 голос
/ 03 октября 2019

Ваша функция закрывается над переменной currentIndex с самого первого рендера. Таким образом, currentIndex всегда равно 0, а setCurrentIndex(currentIndex + 1) всегда будет пытаться установить его на 1.

Один из способов исправить это - убедиться, что вы каждый раз создаете новую функцию и устанавливаете новое время ожидания, используяэта новая функция. Таким образом, каждая новая функция закрывается по последнему значению. Однако есть более простой подход: setCurrentIndex позволяет вам передать в него функцию, которая будет вызываться с любым текущим значением. Затем вы можете рассчитать новое значение на основе этого:

setCurrentIndex(previousVal => {
  if (previousVal < words.length - 1) {
    return previousVal + 1;
  }
  return 0;
});
...