【问题标题】:React countup animation starts immediately after the page loading , should start when scrolled to the component (without jquery)React 计数动画在页面加载后立即开始,应该在滚动到组件时开始(没有 jquery)
【发布时间】:2018-06-26 13:27:37
【问题描述】:

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

【问题讨论】:

    标签: reactjs counter react-animations


    【解决方案1】:

    自去年以来,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 上的动画。

    【讨论】:

    • 这应该是当前版本的答案
    • 如何让它只运行一次而不是每次进入视口?
    • 干净简单。谢谢。
    • 在与尝试其他方法的两个库进行了数小时的斗争后,对我有用。谢谢!
    • findDOMNode is deprecated in StrictMode 和解决方法?
    【解决方案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;

    【讨论】:

    • 第一次渲染后使用状态设置值。它完美地工作!非常感谢你。 :) 但是一个小错误,开始的计数器在结束 count=25 并且动画在滚动到组件时开始。即在 0-25 的动画开始之前还有一小段时间。
    • 你能用更新的代码编辑你的问题吗?将有助于尝试一下,也许可以根据您所拥有的内容提出修复建议
    • 我更新了代码。它与您回答的代码完全相同。使用 state 只做一次“startAnimation”。
    【解决方案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+" />
    

    【讨论】:

      【解决方案4】:

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

      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>

      【讨论】:

        【解决方案5】:

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

        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。阅读最底部的 README。

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

          • 每次滚动时组件进入视口时运行。不回答提出的问题。
          • 嗨@Sushmit我已经编辑了代码你可以看看它,我认为它解决了你的问题
          【解决方案7】:

          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;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-06-05
            • 2012-08-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-17
            • 1970-01-01
            • 2021-07-28
            相关资源
            最近更新 更多