12

I'm looking for a way to animate a counter in React.

For the sake of the example, I have 3 components of the following structure:

  • Master:
    • logicComponent
    • Counter

(Master is the parent of logicComponent and Counter)

The logic component passes a number to the master who passes it to the Counter component that should do the animation. The logicComponent sends the numbers in an incremental manner, that is, each time that something happens there, it sends an update.

For example, the logicCounter invokes the Master ten times to increment the counter, I would've expected that the Counter will be rendered 10 times showing 10 numbers. All the things I've tried so far resulted in showing the final number (10) without any incrementation.

After looking for solutions, I came across Window.requestAnimationFrame() and I'm looking for a proper way to implement it in React.

I'm trying to avoid 3rd party npms/libraries.

Would love your help / ideas. Thanks.

alexunder
  • 2,075
  • 3
  • 16
  • 29
  • 1
    If you already have an implementation (non-working is fine) share it please. Otherwise it'd help to start a bounty on the issue as it requires fresh implementation, you are preferring against reusing existing libraries. – hazardous Dec 29 '15 at 10:39

4 Answers4

12

For an animated counter in React-JS, I use 'react-count' : A configurable React component wrapper around 'CountUp.js'.

Please Refer : https://github.com/glennreyes/react-countup. Check out live demo : https://tr8tk.csb.app/ Steps :

Install :

*npm install react-countup --save*
or
*yarn add react-countup*

Simple Example :

import React from 'react';
import { render } from 'react-dom';
import CountUp from 'react-countup';

render(
  <CountUp start={0} end={160526} />,
  document.getElementById('root')
);

Advanced Example :

import React from 'react';
import { render } from 'react-dom';
import CountUp from 'react-countup';

const onComplete = () => {
  console.log('Completed!');
};

const onStart = () => {
  console.log('Started!');
};

render(
  <CountUp
    className="account-balance"
    start={160527.0127}
    end={-875.0319}
    duration={2.75}
    useEasing={true}
    useGrouping={true}
    separator=" "
    decimals={4}
    decimal=","
    prefix="EUR "
    suffix=" left"
    onComplete={onComplete}
    onStart={onStart}
  />,
  document.getElementById('root'),
);
Muhammed Rahif
  • 434
  • 1
  • 5
  • 17
GPPanda
  • 121
  • 1
  • 5
10

maybe you should try my package, if you are using react.

enter image description here

https://github.com/bluebill1049/react-flip-numbers

i have used it to create the above count down timer.

code example below:

import react from 'react';
import FlipNumbers from 'react-flip-numbers';

export default function SexyComponent(props) {
    return <div>
        <FlipNumbers
            height="12px"
            width="12px"
            color="red"
            background="white"
            startAnimation
            numbers="12345"
            numberStyle={{ color: "black" }}
      />
    </div>;
}
Bill
  • 17,872
  • 19
  • 83
  • 131
1

window.requestAnimationFrame() is mostly for animation optimization on browser. Your can read more about it here: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/

But I don't think it could help you with your issue. From what I understand your problem is with your react components.

Here is an example of a possible implementation of your architecture: https://jsfiddle.net/snahedis/69z2wepo/28572/

var Master = React.createClass({
  increment: function() {
    this.refs.counter.increment();
  },
  render: function() {
    return (
      <div>
        <Counter ref="counter" />
        <Logic increment={this.increment} />
      </div>
    );
  }
});

var Logic = React.createClass({
  render: function() {
    return <button onClick={this.props.increment}>increment</button>;
  }
});

var Counter = React.createClass({
  getInitialState: function() {
    return {
        counter: 0
    };
  },
  increment: function() {
    this.setState({
        counter: this.state.counter + 1
    });
  },
  render: function() {
    return <div>{this.state.counter}</div>;
  }
});

ReactDOM.render(
  <Master />,
  document.getElementById('container')
);

Obviously the increment method on the Logic component could be triggered with something else that a click on a button.

However, this structure is a bit strange. If it's possible I would recommend to change it. Logic component would become the parent of the Counter component rather than his sibling.

Here is the example: https://jsfiddle.net/snahedis/69z2wepo/28573/

var Master = React.createClass({
  render: function() {
    return (
      <div>
        <CounterLogicWrapper />
      </div>
    );
  }
});

var CounterLogicWrapper = React.createClass({
  getInitialState: function() {
    return {
        counter: 0
    };
  },
  increment: function() {
    this.setState({
        counter: this.state.counter + 1
    });
  },  
  render: function() {
    return (
      <div>
        <Counter counter={this.state.counter} />
        <button onClick={this.increment}>increment</button>
      </div>
    );
  }
});

var Counter = React.createClass({
  render: function() {
    return <div>{this.props.counter}</div>;
  }
});

ReactDOM.render(
  <Master />,
  document.getElementById('container')
);
Snahedis
  • 231
  • 1
  • 6
1

Relevant React package, react-animated-counter https://www.npmjs.com/package/react-animated-counter

react-animated-counter demo

tuckermassad
  • 126
  • 4