2

I have a indeterminate linear css loading indicator like the ones in material design. Every time I fetch data from a Redux's reducer, I show this indicator. The problem is that while the indicator shows up, it is not being animated. It looks like the whole pages freezes up until the reducer is populated with the data.

Let's suppose I have my loading indicator visible and animated at all times. As soon as I fetch/request data into a reducer, the loading indicator stops animating.

What do you think is causing this? Is it fixable or Do I need to implement a gif indicator and not a css one?

EDIT 1

This is the my Loader component:

import React, { Component, PropTypes } from 'react';
import radium from 'radium';

// indeterminate Linear (type: indeterminateLinear)
const indeterminateLinearLong = radium.keyframes({
  '0%': {
    left: '-35%',
    right: '100%'
  },
  '60%': {
    left: '100%',
    right: '-90%'
  },
  '100%': {
    left: '100%',
    right: '-90%'
  }
});
const indeterminateLinearShort = radium.keyframes({
  '0%': {
    left: '-200%',
    right: '100%'
  },
  '60%': {
    left: '107%',
    right: '-8%'
  },
  '100%': {
    left: '107%',
    right: '-8%'
  }
});
const indeterminateLinear = {
  base: {
    backgroundColor: '#26a69a',
  },
  progress: {
    position: 'relative',
    height: '4px',
    display: 'block',
    width: '100%',
    backgroundColor: '#acece6',
    // borderRadius: '2px',
    backgroundClip: 'padding-box',
    // margin: '0.5rem 0 1rem 0',
    overflow: 'hidden',
    zIndex: '999'
  },
  before: {
    position: 'absolute',
    backgroundColor: 'yellow',
    top: '0',
    left: '0',
    bottom: '0',
    willChange: 'left, right',
    animation: 'indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite',
    animationName: indeterminateLinearLong
  },
  after: {
    position: 'absolute',
    backgroundColor: 'yellow',
    top: '0',
    left: '0',
    bottom: '0',
    willChange: 'left, right',
    animation: 'indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite',
    animationName: indeterminateLinearShort,
    animationDelay: '1.15s'
  }
};

// Indeterminate Cirular (type: indeterminateCircular (default))
const indeterminateCircularRotate = radium.keyframes({
  '100%': {
    transform: 'rotate(360deg)'
  },
});
const indeterminateCircularColor = radium.keyframes({
  '100%, 0%': {
    stroke: 'red'
  },
  '40%': {
    stroke: 'blue'
  },
  '60%': {
    stroke: 'green'
  },
  '80%, 90%': {
    stroke: 'yellow'
  }
});
const indeterminateCircularDash = radium.keyframes({
  '0%': {
    strokeDasharray: '1,200',
    strokeDashoffset: '0'
  },
  '50%': {
    strokeDasharray: '89,200',
    strokeDashoffset: '-35'
  },
  '100%': {
    strokeDasharray: '89,200',
    strokeDashoffset: '-124'
  },
});
const indeterminateCircular = {
  loader: {
    position: 'absolute',
    width: '100px',
    height: '100px',
    left: '50%',
    top: '20%'
  },
  circular: {
    animation: 'x 2s linear infinite',
    animationName: indeterminateCircularRotate,
    height: '24px',
    position: 'relative',
    width: '24px'
  },
  path: {
    strokeDasharray: '1,200',
    strokeDashoffset: '0',
    animation: 'dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite',
    animationName: indeterminateCircularDash,
    strokeLinecap: 'round',
    stroke: 'red',
    strokeWidth: '2'
  }
}

class Loader extends Component {
  render() {
    const {
      type
    } = this.props;
    let loader = null;

    if ( type === 'indeterminateLinear' ) {
      loader = (
        <div style={ indeterminateLinear.progress }>
          <div style={ indeterminateLinear.before }></div>
          <div style={ indeterminateLinear.base }></div>
          <div style={ indeterminateLinear.after }></div>
        </div>
      );
    } else {
      loader = (
        <div>
          <div style={ indeterminateCircular.loader }>
            <svg style={ indeterminateCircular.circular } viewBox="25 25 50 50">
              <circle style={
                indeterminateCircular.path
              } cx="50" cy="50" r="20" fill="none" strokeMiterlimit="10"/>
            </svg>
          </div>
        </div>
      );
    }

    return (
      <div>{ loader }</div>
    );
  }
}

Loader.propTypes = {
  type: PropTypes.string,
};

export default radium(Loader);

When I include the Loader component, it works and animates without a problem; however, when I request some data to be fetched, the animation stops. I can also notice that everything else frozes up too until React rerenders and shows the fetched data.

This Loader component is being included in an top level component that is connected to the reducers, when data is being fetched, the component is being shown.

{ this.props.globalState.someReducer.isFetching ?
  <Theme render="Loader" type="indeterminateLinear" /> : null
}

EDIT 2

I want to add another use case. This time has nothing to do with redux, so we can remove it from the picture. It has to be with react itself.

I have a page listing some items, approx. 40 items, and I have a button that sorts this list by date.

This is what I have in my render function:

const sortDesc = ( a, b ) => (
    transactions[b].updatedAt ||
    transactions[b].createdAt) -
    (transactions[a].updatedAt ||
    transactions[a].createdAt
)
const sortAsc = ( a, b ) => (
    transactions[a].updatedAt ||
    transactions[a].createdAt) -
    (transactions[b].updatedAt ||
    transactions[b]. createdAt
)

let sortDate = sortDesc;
if (!this.state.sortDesc) {
  sortDate = sortAsc
}

<button onClick={ this.handleSortBy }>sort by date</button>

        {
          Object.keys(transactions)
            .sort(
              sortDate
          ).map(( key, index )=> {

            return (
              <div key={ index }>
                <Payout {...this.props} />
              </div>
            );
          })
        }

I have my Loader component inside a parent component. The structure could be something like this:

-- Layout <-- here we have the Loader

---- Transactions <-- here is the sort function

------ Payout <-- child component

Now, everytime I hit the sort button, everything freezes up until the sorting is finished. When I say "everything" it means all components, including parent components (in this case Layout and Loader).

The Loader is built with Radium.

I must be missing something here, I cannot believe all the components will freeze up when a child componen is re-rendering.

2
  • 2
    how exactly are you animating it? Commented Apr 19, 2016 at 1:21
  • Please include the code for your components. Commented Apr 19, 2016 at 1:43

1 Answer 1

1

I finally solved it. After some research, I learned a lot about web workers just to find out it was not the right solution. Anyways, it has to do on how javascript works. Javascript is single threaded and thus blocking. So when we have some task that takes some time (in my case was a second or so) everything we seen on screen will freeze, even css animations! It does depend on what browser you are using. In my case, i was using Chrome. I tested it firefox and the animations did not stop.

Let's go to the solution. I noticed that Chrome has some issues with transitions, specially with some basic css elements such as top, right, bottom, left, margins... so i had to go with transforms. Not all transforms will work either. For example, translate percentages won't work, so I had to use pixels. For this, i needed to calculate the width of the parent element and do some calculations to be able to translate my child element on the X axis from the left to the right of the screen.

I will update this post with the actual code.

Sign up to request clarification or add additional context in comments.

5 Comments

@ivarni not precisely, i found out it has nothing to do with redux, it's just plain javascript. It takes almost a second for rendering approximately 100 components, this was blocking my css animations for my loader indicator. I just had to make my transitions with transfrom translate (using px) it now works like a charm. I might have to post another question to find out why it is taking so long when rendering my components, that's another topic. Updating the store from fetched data from the server is insanely fast.
Yeah, transform translate will most of the time leverage the GPU directly making them much smoother in general. If I were you I would definetely try to find out why the browsers spend so much time rendering but even if you solve that, hang on to the hardware-accelerated CSS animations.
@ivarni what i could not find out was why the transforms worked on some attributes and not on others. It works on translateX(100px) but it doesn't work on translateX(100%).When I say that don't work, i mean that the animations freeze.
You can try to force hardware acceleration by adding translateZ(0); since IIRC it's always enabled for 3D translations
Nice, i will try that next time. Thank you. Using 3d translations we lose some cross browser support though if i am not mistaken.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.