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

问题描述 投票:0回答:11

我有一个反应单页应用程序,具有多个组件。对于第五个组件(仅在向下滚动时可见),我有一个计数器。现在我正在使用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
11个回答
35
投票

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


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
    end
    start
    ),将它们设置为等于用户向下滚动时更改的某种状态
  • 利用在动画开始之前调用的
    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;


4
投票

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

依赖关系:
1.react-countupv.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+" />

3
投票

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

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>


2
投票

只需添加以下道具

启用ScrollSpy:true

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

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。阅读最底部的自述文件。


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

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;

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

试试这个 滚动间谍应该做这个工作


0
投票

这是使用

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

0
投票

您只需添加 enableScrollSpy 标志即可实现此目的。

<CountUp end={25} enableScrollSpy />

另外,我想在每次焦点进入时启动动画,请添加 scrollSpyOnce 标志并将其设置为 false

<CountUp end={25} enableScrollSpy scrollSpyOnce={false} />
© www.soinside.com 2019 - 2024. All rights reserved.