13

我有一个反应单页应用程序,具有多个组件。对于第 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>)}
}
4

7 回答 7

26

自去年以来,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}这里的道具只是为了在 tabChange 上重绘动画。

于 2019-09-03T15:10:31.240 回答
9

根据React CountUp 的 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根据该更改的状态阻止它再次执行)。

实现相同效果的不太优雅(不推荐)的方法可能包括:

  • 通过将它们设置为等于用户向下滚动时更改的某些状态来使用内置动画触发器(即更改道具duration、、、endstart
  • 利用在onStart动画开始之前调用的道具来延迟开始动画,直到用户向下滚动

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

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

end但是我们可以通过使用 state 来操作 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;

于 2018-06-26T14:20:11.033 回答
3

这是我使用基于类的组件的解决方案注意:导入两个库首先运行此代码 react-visibility-sensor react-countup

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>

于 2021-07-09T05:22:34.067 回答
3

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

依赖项:
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+" />
于 2020-01-17T11:49:27.040 回答
1

你可以看看我的功能组件来实现这个

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>
      );
    }
于 2020-01-08T05:25:46.653 回答
1

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

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。

于 2018-06-26T14:17:03.647 回答
0

react-visibility-sensor目前似乎没有维护,并产生与findDOMNode不推荐使用的相关的警告。

我在我的功能组件中使用了react-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;
于 2022-03-01T17:03:59.540 回答