Анимация обратного отсчета начинается сразу после загрузки страницы, должна начинаться при прокрутке к компоненту (без jquery)
У меня есть приложение реагировать на одну страницу, с несколькими компонентами. Для 5-го компонента (виден только при прокрутке вниз) у меня есть счетчик. Теперь я использую библиотеку реагирования-подсчета для достижения функции счетчика. Однако счетчик запускается сразу после загрузки страницы. Можно ли начать отсчет после прокрутки вниз к компоненту. Анимация происходит только один раз (что хорошо) после загрузки страницы, но я бы хотел, чтобы счетчик запускался не сразу после загрузки страницы, а когда пользователь прокручивает компонент до первого раза. Мой код выглядит так:
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>)}
}
10 ответов
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}
prop здесь только для перерисовки анимации на tabChange.
В README React CountUp вы можете использовать startAnimation
крючок, чтобы вручную запустить анимацию. Добавьте к этому что-то вроде датчика реакции-видимости, и вы можете подождать, чтобы запустить анимацию, пока она не появится в браузере пользователя.
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
prop, вызываемый до запуска анимации, чтобы отложить запуск анимации до тех пор, пока пользователь не прокрутит вниз
РЕДАКТИРОВАТЬ: обновить, чтобы ответить на ваш второй вопрос
К сожалению, похоже react-countup
библиотека не предоставляет способ предотвратить startAnimation
на старте.
Но мы можем взломать довольно элегантное решение, манипулируя end
вместо этого используйте 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;
Вот моя реализация. Он запускается только один раз и не перерисовывает каждый раз, когда компонент входит в область просмотра для проверки видимости.
Зависимости:
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+" />
Здесь основано мое решение с использованием класса компонентов Примечание: импорт две библиотеки первого запуска этого кода реагируют-видимость-датчик реагирует-Countup
Просто добавьте ниже реквизит
enableScrollSpy: правда
<CountUp enableScrollSpy={true} end={75}/>
Вы можете взглянуть на мой функциональный компонент, чтобы добиться этого
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>
);
}
Документы для этой библиотеки позволяют вручную запустить счетчик. Я бы использовал этот подход, чтобы запустить счетчик, как только пользователь прокрутит до нужного расстояния.
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 в самом низу.
<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>
);
}
датчик реакции-видимости , похоже, в данный момент не поддерживается и выдает предупреждения, связанные с
findDOMNode
который устарел.
Я использовал react-waypoint в своем функциональном компоненте, который заставляет анимацию запускаться только один раз, когда пользователь видит дочерний компонент компонента 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;