image-comparison-slider-react
Version:
Image comparison slider component for React.
109 lines (100 loc) • 3.51 kB
JavaScript
import './styles.css';
import React from 'react';
const defaultOptions = {
color: '#2188ff',
iconColor: '#fff',
height: '18.75rem',
width: '30rem',
};
export default class ImageComparisonSlider extends React.Component {
constructor(props) {
super(props);
this.imageRef = React.createRef();
this.state = {
sliderPosition: 50,
dragging: false,
};
this.startDragging = this.startDragging.bind(this);
this.stopDragging = this.stopDragging.bind(this);
this.drag = this.drag.bind(this);
this.dragMobile = this.dragMobile.bind(this);
}
componentDidMount() {
document.addEventListener('mousemove', this.drag);
document.addEventListener('mouseup', this.stopDragging);
// mobile
document.addEventListener('touchend', this.stopDragging);
document.addEventListener('touchmove', this.dragMobile, { passive: false });
}
componentWillUnmount() {
document.removeEventListener('mousemove', this.drag);
document.removeEventListener('mouseup', this.stopDragging);
// mobile
document.removeEventListener('touchend', this.stopDragging);
document.removeEventListener('touchmove', this.dragMobile);
}
stopDragging() {
this.setState({ dragging: false });
}
startDragging() {
console.log('go');
this.setState({ dragging: true });
}
drag(e) {
if (this.state.dragging) {
const rect = this.imageRef.current.getBoundingClientRect();
const width = rect.right - rect.left;
let perc = ((e.clientX - rect.left) / width) * 100;
perc = Math.max(10, perc);
perc = Math.min(90, perc);
this.setState({ sliderPosition: perc });
}
}
dragMobile(e) {
if (this.state.dragging) {
e.preventDefault();
}
this.drag(e.changedTouches[0]);
}
render() {
const { images } = this.props;
if (!images) {
throw new Error("Prop 'images' must be present.");
} else if (images.length !== 2) {
throw new Error("Prop 'images' must have two elements.");
} else if (images.filter((e) => e.hasOwnProperty('src') && e.hasOwnProperty('src')).length !== images.length) {
throw new Error("Every image must have an 'src' property and an 'alt' property.");
}
const options = { ...defaultOptions, ...this.props.options };
return (
<div
className='__ReactImageComparisonSlider'
style={{
'--theme': options.color,
'--icon-color': options.iconColor,
'--left-offset': `${this.state.sliderPosition}%`,
'--height': options.height,
'--width': options.width,
}}
ref={this.imageRef}
>
<img src={images[1].src} alt={images[1].alt} />
<img src={images[0].src} alt={images[0].alt} />
<div onMouseDown={(e) => this.startDragging(e)} onTouchStart={(e) => this.startDragging(e)}>
<div>
<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'>
<path d='M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z' />
</svg>
<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'>
<path d='M7.33 24l-2.83-2.829 9.339-9.175-9.339-9.167 2.83-2.829 12.17 11.996z' />
</svg>
</div>
</div>
<div>
{images[0].text ? <p>{images[0].text}</p> : null}
{images[1].text ? <p>{images[1].text}</p> : null}
</div>
</div>
);
}
}