Skip to content

Instantly share code, notes, and snippets.

@mantaskaveckas
Created January 5, 2017 12:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mantaskaveckas/a58a68b712b0bda5cf3fbfc45026d25d to your computer and use it in GitHub Desktop.
Save mantaskaveckas/a58a68b712b0bda5cf3fbfc45026d25d to your computer and use it in GitHub Desktop.
import React, { Component } from 'react';
// Webkit or not, here I come, you can't hide 🎶
const transformProperty = (() => {
const style = document.documentElement.style;
if (typeof style.transform == 'string') {
return 'transform';
}
return 'WebkitTransform';
})();
class App extends Component {
constructor(options) {
super();
this.options = options;
}
componentDidMount() {
// Merge defaults with user's settings
this.config = Object.assign({
selector: this.selector,
duration: 200,
easing: 'ease-out',
perPage: 1,
startIndex: 0,
draggable: true,
threshold: 20,
loop: false,
}, this.options);
this.selectorWidth = this.selector.getBoundingClientRect().width;
this.innerElements = [].slice.call(this.selector.children);
this.currentSlide = this.config.startIndex;
// Build markup and apply required styling to elements
this.init();
// Resize element on window resize
window.addEventListener('resize', () => {
this.resize();
this.slideToCurrent();
});
// If element is draggable / swipable, add event handlers
// Keep track pointer hold and dragging distance
this.pointerDown = false;
this.drag = {
start: 0,
end: 0,
};
}
init() {
if (this.selector === null) {
throw new Error('Something wrong with your selector 😭');
}
// update perPage number dependable of user value
this.resolveSlidesNumber();
// hide everything out of selector's boundaries
this.selector.style.overflow = 'hidden';
// Create frame and apply styling
this.sliderFrame = document.createElement('div');
this.sliderFrame.style.width = `${(this.selectorWidth / this.perPage) * this.innerElements.length}px`;
this.sliderFrame.style.webkitTransition = `all ${this.config.duration}ms ${this.config.easing}`;
this.sliderFrame.style.transition = `all ${this.config.duration}ms ${this.config.easing}`;
if (this.config.draggable) {
this.sliderFrame.style.cursor = '-webkit-grab';
}
// Create a document fragment to put slides into it
const docFragment = document.createDocumentFragment();
// Loop through the slides, add styling and add them to document fragment
for (let i = 0; i < this.innerElements.length; i++) {
this.innerElements[i].style.cssFloat = 'left';
this.innerElements[i].style.float = 'left';
this.innerElements[i].style.width = `${100 / this.innerElements.length}%`;
docFragment.appendChild(this.innerElements[i]);
}
// Add fragment to frame and frame to selector
this.sliderFrame.appendChild(docFragment);
this.selector.appendChild(this.sliderFrame);
// Go to currently active slide after initial build
this.slideToCurrent();
}
resolveSlidesNumber() {
if (typeof this.config.perPage === 'number') {
this.perPage = this.config.perPage;
}
else if (typeof this.config.perPage === 'object') {
this.perPage = 1;
for (const viewport in this.config.perPage) {
if (window.innerWidth > viewport) {
this.perPage = this.config.perPage[viewport];
}
}
}
}
prev() {
if (this.currentSlide === 0 && this.config.loop) {
this.currentSlide = this.innerElements.length - this.perPage;
}
else {
this.currentSlide = Math.max(this.currentSlide - 1, 0);
}
this.slideToCurrent();
}
next() {
if (this.currentSlide === this.innerElements.length - this.perPage && this.config.loop) {
this.currentSlide = 0;
}
else {
this.currentSlide = Math.min(this.currentSlide + 1, this.innerElements.length - this.perPage);
}
this.slideToCurrent();
}
goTo(index) {
this.currentSlide = Math.min(Math.max(index, 0), this.innerElements.length - 1);
this.slideToCurrent();
}
slideToCurrent() {
this.sliderFrame.style[transformProperty] = `translate3d(-${this.currentSlide * (this.selectorWidth / this.perPage)}px, 0, 0)`;
}
updateAfterDrag() {
const movement = this.drag.end - this.drag.start;
if (movement > 0 && Math.abs(movement) > this.config.threshold) {
this.prev();
}
else if (movement < 0 && Math.abs(movement) > this.config.threshold) {
this.next();
}
this.slideToCurrent();
}
resize() {
// update perPage number dependable of user value
this.resolveSlidesNumber();
this.selectorWidth = this.selector.getBoundingClientRect().width;
this.sliderFrame.style.width = `${(this.selectorWidth / this.perPage) * this.innerElements.length}px`;
}
clearDrag() {
this.drag = {
start: 0,
end: 0,
};
}
touchstartHandler(e) {
e.stopPropagation();
this.pointerDown = true;
this.drag.start = e.touches[0].pageX;
}
touchendHandler(e) {
e.stopPropagation();
this.pointerDown = false;
this.sliderFrame.style.webkitTransition = `all ${this.config.duration}ms ${this.config.easing}`;
this.sliderFrame.style.transition = `all ${this.config.duration}ms ${this.config.easing}`;
if (this.drag.end) {
this.updateAfterDrag();
}
this.clearDrag();
}
touchmoveHandler(e) {
e.stopPropagation();
if (this.pointerDown) {
this.drag.end = e.touches[0].pageX;
this.sliderFrame.style.webkitTransition = `all 0ms ${this.config.easing}`;
this.sliderFrame.style.transition = `all 0ms ${this.config.easing}`;
this.sliderFrame.style[transformProperty] = `translate3d(${(this.currentSlide * (this.selectorWidth / this.perPage) + (this.drag.start - this.drag.end)) * -1}px, 0, 0)`;
}
}
mousedownHandler(e) {
e.preventDefault();
e.stopPropagation();
this.pointerDown = true;
this.drag.start = e.pageX;
}
mouseupHandler(e) {
e.stopPropagation();
this.pointerDown = false;
this.sliderFrame.style.cursor = '-webkit-grab';
this.sliderFrame.style.webkitTransition = `all ${this.config.duration}ms ${this.config.easing}`;
this.sliderFrame.style.transition = `all ${this.config.duration}ms ${this.config.easing}`;
if (this.drag.end) {
this.updateAfterDrag();
}
this.clearDrag();
}
mousemoveHandler(e) {
e.preventDefault();
if (this.pointerDown) {
this.drag.end = e.pageX;
this.sliderFrame.style.cursor = '-webkit-grabbing';
this.sliderFrame.style.webkitTransition = `all 0ms ${this.config.easing}`;
this.sliderFrame.style.transition = `all 0ms ${this.config.easing}`;
this.sliderFrame.style[transformProperty] = `translate3d(${(this.currentSlide * (this.selectorWidth / this.perPage) + (this.drag.start - this.drag.end)) * -1}px, 0, 0)`;
}
}
mouseleaveHandler(e) {
if (this.pointerDown) {
this.pointerDown = false;
this.sliderFrame.style.cursor = '-webkit-grab';
this.drag.end = e.pageX;
this.sliderFrame.style.webkitTransition = `all ${this.config.duration}ms ${this.config.easing}`;
this.sliderFrame.style.transition = `all ${this.config.duration}ms ${this.config.easing}`;
this.updateAfterDrag();
this.clearDrag();
}
}
render() {
return (
<div
ref={(selector) => this.selector = selector}
onTouchStart={this.touchstartHandler.bind(this)}
onTouchEnd={this.touchendHandler.bind(this)}
onTouchMove={this.touchmoveHandler.bind(this)}
onMouseDown={this.mousedownHandler.bind(this)}
onMouseUp={this.mouseupHandler.bind(this)}
onMouseLeave={this.mouseleaveHandler.bind(this)}
onMouseMove={this.mousemoveHandler.bind(this)}
>
<div>Hi, I'm slide 1</div>
<div>Hi, I'm slide 2</div>
<div>Hi, I'm slide 3</div>
<div>Hi, I'm slide 4</div>
</div>
);
}
}
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment