React计数动画在页面加载后立即开始,滚动到组件时应开始(没有jquery)

时间:2018-06-26 13:27:38

标签: reactjs counter react-animations

我有一个带有多个组件的React单页应用程序。对于第5个组件(仅向下滚动时可见),我有一个counter。现在我正在使用react-countup库来实现计数器功能。但是,计数器将在页面加载后立即启动。向下滚动到组件后,是否可以开始计数。 动画在页面加载后仅发生一次(这很好),但是我希望计数器在页面加载后不立即开始,而是在用户第一次向下滚动到组件时开始。 我的代码如下:

    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>)}
}

6 个答案:

答案 0 :(得分:2)

对于每个React CountUp's README,您可以使用startAnimation钩子手动启动动画。将其与react-visibility-sensor之类的东西结合使用,您可以等待动画开始,直到在用户浏览器中可见为止。

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)即可。

为达到相同效果而采取的优雅方法(不推荐)包括:

  • 通过将内置动画触发器(例如,更改道具durationendstart)设置为等于用户向下滚动时会改变的某种状态来使用
  • >
  • 利用onStart道具(在动画开始之前调用)来延迟动画的开始,直到用户向下滚动为止

编辑:更新以解决您的第二个问题

不幸的是,react-countup库似乎没有公开阻止startAnimation启动的方法。

但是我们可以通过使用state来操纵end道具,从而获得一个相当优雅的解决方案:

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 :(得分:2)

自去年以来,API可能已更改。我现在设法用此代码来完成这项工作:

import React from "react";
import CountUp from "react-countup";

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

export default App;

我在标签内使用此组件,因此redraw={true}道具仅在此处用于在tabChange上重绘动画。

答案 2 :(得分:2)

这是我使用基于类的组件的解决方案 注意:导入两个库先运行这段代码 反应可见性传感器 反应计数

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

class CountDown extends React.Component {
    render() {
        return (
            
                <React.Fragment>
                    <CountUp start={0} end={100} prefix="+" duration="2">

                        {({ countUpRef, start }) => (
                            <VisibilitySensor onChange={start} delayedCall>
                                <span ref={countUpRef} />
                            </VisibilitySensor>
                        )}

                    </CountUp>
                </React.Fragment>
           

        )
    }
}

export default CountDown;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

答案 3 :(得分:1)

这是我的实现。它只运行一次,并且每次组件进入视口以检查可见性时都不会重新渲染。

依赖项:
1. 反应计数 v.4.3.2
2. 反应可见性传感器 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+" />

答案 4 :(得分:0)

该库的文档可以手动启动计数器。一旦用户滚动到所需距离,我将使用该方法启动计数器。

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;

Link to Github。阅读自述文件的最底部。

答案 5 :(得分:0)

您可以查看我的功能组件来实现

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>
      );
    }