Skip to content

Instantly share code, notes, and snippets.

@markus-willems
Last active November 2, 2018 09:11
Show Gist options
  • Save markus-willems/82a96e07f0295c9c896aac84df5714da to your computer and use it in GitHub Desktop.
Save markus-willems/82a96e07f0295c9c896aac84df5714da to your computer and use it in GitHub Desktop.
Simple progress bar React component

Usage

Progress bar takes 10 seconds to finish.

<SimpleProgressBar duration={10} />

Progress bar takes 5 seconds to finish. Progress starts at 50 % (i.e. already 5s in progress).

<SimpleProgressBar startAt={5} duration={10} />

Add customclassName and / orid.

<SimpleProgressBar id="custom-id" className="custom-class-name" duration={10} />

Demo

Check out a demo at CodeSandbox.

import React from "react";
class SimpleProgressBar extends React.Component {
rafId = null;
defaultId = "progress-bar";
defaultClassName = "progress-bar";
constructor(props) {
super(props);
// Used to determine initial width when `startAt` is set
const initialWidth = props.startAt
? (props.startAt / props.duration) * 100
: 0;
this.state = {
width: initialWidth
};
this.startProgress = this.startProgress.bind(this);
}
componentDidMount() {
const duration = this.props.duration || 0;
const startAt = this.props.startAt || 0;
const progressbarId = this.props.id ? this.props.id : this.defaultId;
const progressbarEl = document.getElementById(progressbarId);
// Width of direct parent of `progressbarEl`
const parentWidth = progressbarEl.parentNode.clientWidth;
// Percent value that is added every second to the initial width
const widthStepInPercent = (parentWidth / duration / parentWidth) * 100;
this.startProgress(duration - startAt, () => {
this.setState(prevState => ({
width: prevState.width + widthStepInPercent
}));
});
}
componentWillUnmount() {
window.cancelAnimationFrame(this.rafId);
}
startProgress(duration, updateProgress) {
let currentSecond = 1;
let second = 1000;
let startTimestamp = performance.now();
// Start progressing as soon as the component mounted
updateProgress();
const step = timestamp => {
// Branch is entered whenever a second passed
if (timestamp - startTimestamp >= second) {
currentSecond += 1;
updateProgress();
startTimestamp = timestamp;
}
window.cancelAnimationFrame(this.rafId);
// Execute `step` callback again if progress bar hasn't finished
if (currentSecond < duration) {
this.rafId = window.requestAnimationFrame(step);
}
};
this.rafId = window.requestAnimationFrame(step);
}
render() {
const { className, id } = this.props;
return (
<div
style={{
width: `${this.state.width}%`,
transition: "width 1s linear"
}}
id={id ? id : this.defaultId}
className={className ? className : this.defaultClassName}
/>
);
}
}
export default SimpleProgressBar;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment