Created
January 5, 2017 12:20
-
-
Save mantaskaveckas/a58a68b712b0bda5cf3fbfc45026d25d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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