Анимация обратного отсчета начинается сразу после загрузки страницы, должна начинаться при прокрутке к компоненту (без jquery)

У меня есть приложение реагировать на одну страницу, с несколькими компонентами. Для 5-го компонента (виден только при прокрутке вниз) у меня есть счетчик. Теперь я использую библиотеку реагирования-подсчета для достижения функции счетчика. Однако счетчик запускается сразу после загрузки страницы. Можно ли начать отсчет после прокрутки вниз к компоненту. Анимация происходит только один раз (что хорошо) после загрузки страницы, но я бы хотел, чтобы счетчик запускался не сразу после загрузки страницы, а когда пользователь прокручивает компонент до первого раза. Мой код выглядит так:

    render() {
         return (
         <div className={style.componentName}>
         <h2>Heading</h2>
         <div className={style.col}>
         <div>My counter</div>
         <CountUp className={style.countup} decimals={1} start={0} end={25} suffix=" %" duration={3} />
        </div>
        </div>)}

Обновленный код:

    import CountUp, { startAnimation } from 'react-countup';
    import VisibilitySensor from 'react-visibility-sensor';

    class className extends Component {

        state = {
            scrollStatus: true
        };


        onVisibilityChange = isVisible => {
            if (isVisible) {
                if (this.state.scrollStatus) {
                    startAnimation(this.myCountUp);
                    this.setState({ scrollStatus: false });
                }
            }
        }
    render() {
             return (
            <div className={style.componentName}>
             <h2>Heading</h2>
             <VisibilitySensor onChange={this.onVisibilityChange} offset = {{ top: 
              10}} delayedCall>
             <CountUp className={style.countup} decimals={1} start={0} end={25} 
             suffix=" %" duration={3} ref={countUp => { this.myCountUp= countUp;}}/>
             </VisibilitySensor>
            </div>)}
}

10 ответов

API, возможно, изменился с прошлого года. Теперь мне удается заставить эту работу работать с этим кодом:

import React from "react";
import CountUp from "react-countup";
import VisibilitySensor from 'react-visibility-sensor';

const MyComponent = () => (
  <>
    <CountUp end={100} redraw={true}>
        {({ countUpRef, start }) => (
            <VisibilitySensor onChange={start} delayedCall>
                <span ref={countUpRef} />
            </VisibilitySensor>
        )}
    </CountUp>
  </>
);

export default App;

Я использую этот компонент внутри вкладки, поэтому redraw={true} prop здесь только для перерисовки анимации на tabChange.

В README React CountUp вы можете использовать startAnimation крючок, чтобы вручную запустить анимацию. Добавьте к этому что-то вроде датчика реакции-видимости, и вы можете подождать, чтобы запустить анимацию, пока она не появится в браузере пользователя.

import React, {Component} from 'react';
import CountUp, {startAnimation} from 'react-countup';
import './App.css';
import VisibilitySensor from 'react-visibility-sensor';

const style = {
  componentName: {},
  col: {},
  countup: {},
};

class App extends Component {
  constructor(props) {
    super(props);
    this.onVisibilityChange = this.onVisibilityChange.bind(this); // Bind for appropriate 'this' context
  }

  onVisibilityChange(isVisible) {
    if (isVisible) {
      startAnimation(this.myCountUp);
    }
  }

  render() {
    return (
      <div className={style.componentName}>
        <h2>Heading</h2>
        <div className={style.col}>
          <div>My counter</div>
          <VisibilitySensor
            onChange={this.onVisibilityChange}
            delayedCall // Prevents react apps triggering elements as visible before styles are loaded
          >
            <CountUp className={style.countup} decimals={1} start={0} end={25} suffix=" %" duration={3}
                     ref={countUp => { this.myCountUp = countUp; }} // From react-countup README 
            />
          </VisibilitySensor>
        </div>
      </div>
    );
  }
}

export default App;

Как и будет startAnimation каждый раз, когда вы прокручиваете счет. Если вы хотите сделать это только один раз, просто добавьте часть состояния, которая будет установлена ​​после первого рендеринга (и затем предотвратите это startAnimation снова на основе этого измененного состояния).

Менее изящные (не рекомендуемые) способы достижения того же эффекта могут включать:

  • Используйте встроенные анимационные триггеры (например, смена реквизита duration, end, start) установив их равными некоторому состоянию, которое изменяется, когда пользователь прокручивает
  • Используя onStart prop, вызываемый до запуска анимации, чтобы отложить запуск анимации до тех пор, пока пользователь не прокрутит вниз

РЕДАКТИРОВАТЬ: обновить, чтобы ответить на ваш второй вопрос

К сожалению, похоже react-countup библиотека не предоставляет способ предотвратить startAnimation на старте.

Но мы можем взломать довольно элегантное решение, манипулируя end вместо этого используйте prop:

import React, {Component} from 'react';
import CountUp, {startAnimation} from 'react-countup';
import './App.css';
import VisibilitySensor from 'react-visibility-sensor';

const style = {
  componentName: {},
  col: {},
  countup: {},
};

class App extends Component {
  state = {
    didViewCountUp: false
  };


  onVisibilityChange = isVisible => {
    if (isVisible) {
      this.setState({didViewCountUp: true});
    }
  }

  render() {
    return (
      <div className={style.componentName}>
        <h2 style={{fontSize: '40em'}}>Heading</h2>
        <VisibilitySensor onChange={this.onVisibilityChange} offset={{
          top:
            10
        }} delayedCall>
          <CountUp className={style.countup} decimals={1} start={0} end={this.state.didViewCountUp ? 25 : 0}
                   suffix=" %" duration={3} />
        </VisibilitySensor>
      </div>)
  }
}

export default App;

Вот моя реализация. Он запускается только один раз и не перерисовывает каждый раз, когда компонент входит в область просмотра для проверки видимости.

Зависимости:
1. react - countup v.4.3.2
2. react -visibility-sensor v.5.1.1.

import React, { useState } from "react";
import CountUp from "react-countup";
import VisibilitySensor from "react-visibility-sensor";

const Ticker = ({ className, ...rest }) => {
  const [viewPortEntered, setViewPortEntered] = useState(false);

  return (
    <CountUp {...rest} start={viewPortEntered ? null : 0}>
      {({ countUpRef }) => {
        return (
          <VisibilitySensor
            active={!viewPortEntered}
            onChange={isVisible => {
              if (isVisible) {
                setViewPortEntered(true);
              }
            }}
            delayedCall
          >
            <h4 className={className} ref={countUpRef} />
          </VisibilitySensor>
        );
      }}
    </CountUp>
  );
};

export default Ticker;

Вот как им пользоваться:

<Ticker className="count" end={21} suffix="M+" />

Здесь основано мое решение с использованием класса компонентов Примечание: импорт две библиотеки первого запуска этого кода реагируют-видимость-датчик реагирует-Countup

Просто добавьте ниже реквизит

enableScrollSpy: правда

      <CountUp enableScrollSpy={true} end={75}/>

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

import React from "react";
    import { Box } from "@material-ui/core";
    import CountUp from "react-countup";
    import VisibilitySensor from "react-visibility-sensor";

    export default function Counter() {
      const [focus, setFocus] = React.useState(false);
      return (
        <Box component="div">
          <CountUp start={focus ? 0 : null} end={100} duration={5} redraw={true}>
            {({ countUpRef }) => (
              <div>
                <span ref={countUpRef} />
                <VisibilitySensor
                  onChange={isVisible => {
                    if (isVisible) {
                      setFocus(true);
                    } 
                  }}
                >
                  <a>+</a>
                </VisibilitySensor>
              </div>
            )}
          </CountUp>
        </Box>
      );
    }

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

import React, { Component } from 'react';
import CountUp, { startAnimation } from 'react-countup';

const MyComponent = () => (
  <div>
    <CountUp className="CountUp" start={0} end={100} duration={3} ref={(countUp) => {
      this.myCountUp = countUp;
    }} />
    <button className="Button" onClick={(event) => {
      startAnimation(this.myCountUp);
    }}>Count me up!</button>
  </div>
);

export default App;

Ссылка на Github. Читайте README в самом низу.

      <CountUp
        className={styles.number}
        start={0}
        end={number}
        enableScrollSpy={true}
        scrollSpyDelay={0}
        suffix={index !== -1 ? "+" : ""}
/>

попробуйте этот шпион прокрутки должен сделать работу

Вот альтернативное решение, использующееreact-in-viewportупаковка. Идея состоит в том, чтобы определить этот элемент и использовать<CountUpInViewport>вместо<CountUp>.

Этот пример срабатывает при первой записи. Можно сделать вариации на этот счет, чтобы перезапускать каждый раз, когда он входит/выходит из области просмотра.

      
import React from 'react';
import { useRef } from 'react';
import CountUp from 'react-countup';
import { useInViewport } from 'react-in-viewport';

let CountUpInViewport = props => {
  const ref = useRef();
  const { enterCount } = useInViewport(ref);
  return (
    <div ref={ref}>
      {
        enterCount > 0 ?
        <CountUp key='in' {...props}></CountUp> :
        <CountUp key='out' {...props}></CountUp>  // ensures drawn when not visible
      }
    </div>
  );
}

датчик реакции-видимости , похоже, в данный момент не поддерживается и выдает предупреждения, связанные с findDOMNodeкоторый устарел.

Я использовал react-waypoint в своем функциональном компоненте, который заставляет анимацию запускаться только один раз, когда пользователь видит дочерний компонент компонента waypoint.

          // Ticker.tsx

import React, { useState } from "react";
import CountUp from "react-countup";
import { Waypoint } from "react-waypoint";

type TickerProps = {
  end: number;
  suffix: string;
}

const Ticker: React.FC<TickerProps> = ({ end, suffix }) => {
  const [viewPortEntered, setViewPortEntered] = useState(false);

  const onVWEnter = () => {
    setViewPortEntered(true);
  }

    return (
      <Waypoint onEnter={onVWEnter} >
        <div>
          {viewPortEntered && <CountUp end={end} suffix={suffix} start={0} /> }
        </div>
      </Waypoint>
    );
};

export default Ticker;
Другие вопросы по тегам