Optimize react typescript glowing spinner

Viewed 28

I have this html/css spinner that I have redesigned as a react component, however it is affecting the other client-side processing making it horribly slow. Below is an example of what it looked like (left: when disabled and right: when spinning)

enter image description here

Here is what I have so far:

import React, { FC, CSSProperties } from 'react';
import './GlowingSpinner.css';

const defaultProps = {
  animate: true
};
type SpinnerProps = Partial<typeof defaultProps>;

const glowingSpinner: FC<SpinnerProps> = ({
  animate,
}) => {

  const animateClass = animate ? 'spinAnimation' : '';

  return (
    <React.Fragment>
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
      <i><b className={animateClass}></b></i>{/*1*/}<i><b className={animateClass}></b></i>{/*2*/}<i><b className={animateClass}></b></i>{/*3*/}<i><b className={animateClass}></b></i>{/*4*/}<i><b className={animateClass}></b></i>{/*5*/}<i><b className={animateClass}></b></i>{/*6*/}<i><b className={animateClass}></b></i>{/*7*/}<i><b className={animateClass}></b></i>{/*8*/}<i><b className={animateClass}></b></i>{/*9*/}<i><b className={animateClass}></b></i>{/*10*/}
    </React.Fragment>
  );
};

export default glowingSpinner;

css:

  .spinner i {
    display: block;
    position: absolute;
    opacity: 1;
    margin-top: -3px;
    margin-left: -4px;
  }

  .spinner i b {
    display: block;
    width: 6px;
    height: 6px;
    border-radius: 3px;
    background: white;
    box-shadow: 0px 0px 14px white;
  }

  .spinAnimation {
    animation-name: spin;
    animation-duration: 3s;
    animation-iteration-count: infinite;
    animation-timing-function: ease-in-out;
  }

  .spinAnimationOff {
    animation-name: none;
  }

  @keyframes spin {
    0% {
      transform: scale(1);
    }

    15% {
      transform: translate(-3px, -3px) scale(3);
    }

    50% {
      transform: scale(1);
    }
  }

  .spinner i:nth-child(1) {
    transform: rotate(3.6deg) translate3d(100px, 0, 0);
  }

  .spinner i:nth-child(1) b {
    animation-delay: 0.03061s;
  }

  .spinner i:nth-child(2) {
    transform: rotate(7.2deg) translate3d(100px, 0, 0);
  }

  .spinner i:nth-child(2) b {
    animation-delay: 0.06122s;
  }

  .spinner i:nth-child(3) {
    transform: rotate(10.8deg) translate3d(100px, 0, 0);
  }

  .spinner i:nth-child(3) b {
    animation-delay: 0.09184s;
  }

  .spinner i:nth-child(4) {
    transform: rotate(14.4deg) translate3d(100px, 0, 0);
  }

  .spinner i:nth-child(4) b {
    animation-delay: 0.12245s;
  }
...

  .spinner i:nth-child(98) {
    transform: rotate(352.8deg) translate3d(100px, 0, 0);
  }

  .spinner i:nth-child(98) b {
    animation-delay: 3s;
  }

  .spinner i:nth-child(99) {
    transform: rotate(356.4deg) translate3d(100px, 0, 0);
  }

  .spinner i:nth-child(99) b {
    animation-delay: 3.03061s;
  }

  .spinner i:nth-child(100) {
    transform: rotate(360deg) translate3d(100px, 0, 0);
  }

  .spinner i:nth-child(100) b {
    animation-delay: 3.06122s;
  }

Implementation as follows with State.isLoading:

  <div id="Spinner" className={styles['spinner']}>
    <GlowingSpinner animate={isLoading} />
  </div>

It was made up of x100 elements and starts/stops when you toggle the class spinAnimation. As it has x100 animations running I'm guessing that this is the issue.

I really like the look of the spinner, so I'm looking for advice on how to reconfigure this component without it affecting performance.

Open to suggestions

1 Answers

I won't give you a full solution to your performance issue, but perhaps something that is easier to build on for others (with some improvements already in place).

The main thing I wanted to achieve was remove duplicate code, so instead of copy pasting 100 lines in TSX and CSS, I'd suggest:

Code-sandbox

TSX:

import React, { FC, CSSProperties } from "react";
import "./GlowingSpinner.css";

const defaultProps = {
  animate: true
};
type SpinnerProps = Partial<typeof defaultProps>;

const glowingSpinner: FC<SpinnerProps> = ({ animate }) => {
  const animateClass = animate ? "spinAnimation" : "";

  // An array with elements from from 1 to 100 (note this could be replaced by an array that you push components to)
  const elements = Array.from(Array(100).keys());

  return (
    <React.Fragment>
      {elements.map((e, i) => (
        <i style={{ transform: `rotate(${3.6 * i}deg) translate3d(100px, 0, 0)` }}>
          <b style={{ animationDelay: `${0.03061 * i}s` }} className={animateClass}></b>
        </i>
      ))}
    </React.Fragment>
  );
};

export default glowingSpinner;

CSS:

.spinner i {
  display: block;
  position: absolute;
  opacity: 1;
  margin-top: -3px;
  margin-left: -4px;
}

.spinner i b {
  display: block;
  width: 6px;
  height: 6px;
  border-radius: 3px;
  background: white;
  box-shadow: 0px 0px 14px white;
}

.spinAnimation {
  animation-name: spin;
  animation-duration: 3s;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
}

.spinAnimationOff {
  animation-name: none;
}

@keyframes spin {
  0% {
    transform: scale(1);
  }

  15% {
    transform: translate(-3px, -3px) scale(3);
  }

  50% {
    transform: scale(1);
  }
}

Note: this code sandbox runs pretty smoothly on my machine. So I'm guessing your issue is with slow machines or phones?

Related