我有一个反应单页应用程序,具有多个组件。对于第五个组件(仅在向下滚动时可见),我有一个计数器。现在我正在使用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>)}
}
自去年以来,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 上重绘动画。
根据 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;
这是我的实现。它只运行一次,并且不会在每次组件进入视口以检查可见性时重新渲染。
依赖关系:
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+" />
这是我使用基于类的组件的解决方案 注意:导入两个库首先运行此代码 反应可见性传感器 反应计数
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>
只需添加以下道具
启用ScrollSpy:true
<CountUp enableScrollSpy={true} end={75}/>
该库的文档有一种手动启动计数器的方法。一旦用户滚动到所需的距离,我将使用这种方法来启动计数器。
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。阅读最底部的自述文件。
你可以看看我的功能组件来实现这个
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>
);
}
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;
<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>
);
}
您只需添加 enableScrollSpy 标志即可实现此目的。
<CountUp end={25} enableScrollSpy />
另外,我想在每次焦点进入时启动动画,请添加 scrollSpyOnce 标志并将其设置为 false。
<CountUp end={25} enableScrollSpy scrollSpyOnce={false} />