Shamoon
Shamoon

Reputation: 43491

How can I smoothly animate a Material UI LinearProgress over a fixed time period?

I am trying to animate over 60 seconds and here's what I have so far:

export default function TimeLoader({ timeout }: ITimeLoaderProps) {
  const [progress, setProgress] = useState(1);

  useInterval(() => {
    setProgress(p => Math.min(p * 1.1, 100));
  }, 100);
  console.log({ progress });


  return (
    <LinearProgress variant="determinate" value={progress} />
  );
}

But obviously this isn't done over 60 seconds. It finishes rather quick. If I increase the timeout time from 100 to something else, then it's a bit jerky.

Thanks SO!

Upvotes: 2

Views: 3781

Answers (1)

Ryan Cogswell
Ryan Cogswell

Reputation: 80986

The example below uses CSS rather than JS to animate the progress bar over 60 seconds. It accomplishes this by using the indeterminate variant, but then customizing its CSS. The indeterminate variant leverages two bars in its animation. This example suppresses the second bar and changes the first bar to animate over 60 seconds once instead of 2.1 seconds infinitely repeating. This example also changes the keyframes part of the animation so that it ends with a full bar, rather than ending with the bar disappearing. Using forwards in the animation causes the final state to stick when the animation finishes.

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import LinearProgress from "@material-ui/core/LinearProgress";

const useStyles = makeStyles({
  root: {
    width: "100%"
  },
  bar1Indeterminate: {
    width: "auto",
    animation: "$indeterminate1 60s linear forwards"
  },
  bar2Indeterminate: {
    display: "none"
  },
  "@keyframes indeterminate1": {
    "0%": {
      left: "-35%",
      right: "100%"
    },
    "100%": {
      left: "0%",
      right: "0%"
    }
  }
});

export default function LinearDeterminate() {
  const classes = useStyles();

  return (
    <div className={classes.root}>
      <LinearProgress
        classes={{
          bar1Indeterminate: classes.bar1Indeterminate,
          bar2Indeterminate: classes.bar2Indeterminate
        }}
        variant="indeterminate"
      />
    </div>
  );
}

Edit 60 second progress

Relevant documentation:


Here's an equivalent example using v5 of Material-UI leveraging styled and Emotion's keyframes:

import React from "react";
import { styled } from "@material-ui/core/styles";
import LinearProgress from "@material-ui/core/LinearProgress";
import { keyframes } from "@emotion/react";

const indeterminate1Keyframes = keyframes({
  "0%": {
    left: "-35%",
    right: "100%"
  },
  "100%": {
    left: "0%",
    right: "0%"
  }
});

const StyledLinearProgress = styled(LinearProgress)({
  "& .MuiLinearProgress-bar1Indeterminate": {
    width: "auto",
    animation: `${indeterminate1Keyframes} 60s linear forwards`
  },
  "& .MuiLinearProgress-bar2Indeterminate": {
    display: "none"
  }
});

export default function LinearDeterminate() {
  return (
    <div style={{ width: "100%" }}>
      <StyledLinearProgress variant="indeterminate" />
    </div>
  );
}

Edit 60 second progress

Upvotes: 2

Related Questions