Skip to content

Instantly share code, notes, and snippets.

@Beaglefoot
Created August 21, 2018 18:03
Show Gist options
  • Save Beaglefoot/388e4622204772d182710c7e795fe5d5 to your computer and use it in GitHub Desktop.
Save Beaglefoot/388e4622204772d182710c7e795fe5d5 to your computer and use it in GitHub Desktop.
Simple React Slider
console.clear();
class Slider extends React.Component {
state = { focusedImgIndex: 0, isPlaying: true };
playIntervalId = null;
forceUpdateBound = () => this.forceUpdate();
componentDidMount() {
if (this.props.interval) {
this.playIntervalId = setInterval(
() => {
if (this.state.isPlaying) this.selectImage(this.state.focusedImgIndex + 1)
},
this.props.interval
)
}
window.addEventListener('resize', this.forceUpdateBound);
}
componentWillUnmount() {
clearInterval(this.playIntervalId);
window.removeEventListener('resize', this.forceUpdateBound);
}
selectImage(focusedImgIndex) {
if (focusedImgIndex >= this.props.images.length) focusedImgIndex = 0;
else if (focusedImgIndex < 0) focusedImgIndex = this.props.images.length - 1;
this.setState(prevState => ({ ...prevState, focusedImgIndex }));
}
getOffset(focusedImgIndex) {
const { images } = this.props;
// Case where viewport is shorter than current image
const toCenter = window.innerWidth < images[focusedImgIndex].width
? (images[focusedImgIndex].width - window.innerWidth) / 2
: 0;
return images
.slice(0, focusedImgIndex)
.reduce((offset, { width }) => offset + width, 0) + toCenter;
}
render() {
return (
<div className="slider">
<div
className="images-line"
style={{ transform: `translateX(-${this.getOffset(this.state.focusedImgIndex)}px)` }}
>
{this.props.images.map(({ src }) => <img src={src} />)}
</div>
<i
className="arrow arrow-left fas fa-caret-left"
onClick={() => this.selectImage(this.state.focusedImgIndex - 1)}
/>
<i
className="arrow arrow-right fas fa-caret-right"
onClick={() => this.selectImage(this.state.focusedImgIndex + 1)}
/>
<div className="dots">
{this.props.images.map((_, i) => (
<div
className={classNames('dot', this.state.focusedImgIndex === i && 'dot-selected')}
onClick={() => this.selectImage(i)}
/>
))}
</div>
<i
class={classNames('play-pause', 'far', this.state.isPlaying ? 'fa-pause-circle' : 'fa-play-circle')}
onClick={() => this.setState(
prevState => ({ ...prevState, isPlaying: !prevState.isPlaying })
)}
/>
</div>
);
}
}
ReactDOM.render(
<Slider
images={window.images.map(src => { // these are loaded as data:url from another pen
const img = new Image();
img.src = src;
return img;
})}
interval={3000}
/>,
document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>
<script src="https://codepen.io/Beaglefoot/pen/wjLKza.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.min.js"></script>
$elements_color: #fff;
body {
font-size: 40px;
}
.slider {
margin: 0 auto;
max-width: 512px;
overflow: hidden;
position: relative;
}
.images-line {
display: flex;
transition: transform 0.5s;
}
.arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
color: $elements_color;
}
.arrow-left {
left: 1rem;
}
.arrow-right {
right: 1rem;
}
.dots {
display: flex;
position: absolute;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
}
.arrow, .dot, .play-pause {
opacity: 0.6;
transition: opacity 0.4s;
cursor: pointer;
&:hover {
opacity: 1;
}
}
.dot {
width: 0.8rem;
height: 0.8rem;
border-radius: 100%;
margin: 0 2px;
background: $elements_color;
box-sizing: border-box;
}
.dot-selected {
background: deepskyblue;
}
.play-pause {
position: absolute;
font-size: 1.2rem;
color: $elements_color;
bottom: 1rem;
right: 0;
transform: translateX(-100%);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment