@glidejs/glide
Version:
Glide.js is a dependency-free JavaScript ES6 slider and carousel. It’s lightweight, flexible and fast. Designed to slide. No less, no more
276 lines (227 loc) • 6.47 kB
JavaScript
import { throttle } from '../utils/wait'
import { toInt, toFloat } from '../utils/unit'
import supportsPassive from '../utils/detect-passive-event'
import EventsBinder from '../core/event/events-binder'
const START_EVENTS = ['touchstart', 'mousedown']
const MOVE_EVENTS = ['touchmove', 'mousemove']
const END_EVENTS = ['touchend', 'touchcancel', 'mouseup', 'mouseleave']
const MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'mouseleave']
export default function (Glide, Components, Events) {
/**
* Instance of the binder for DOM Events.
*
* @type {EventsBinder}
*/
const Binder = new EventsBinder()
let swipeSin = 0
let swipeStartX = 0
let swipeStartY = 0
let disabled = false
const capture = (supportsPassive) ? { passive: true } : false
const Swipe = {
/**
* Initializes swipe bindings.
*
* @return {Void}
*/
mount () {
this.bindSwipeStart()
},
/**
* Handler for `swipestart` event. Calculates entry points of the user's tap.
*
* @param {Object} event
* @return {Void}
*/
start (event) {
if (!disabled && !Glide.disabled) {
this.disable()
const swipe = this.touches(event)
swipeSin = null
swipeStartX = toInt(swipe.pageX)
swipeStartY = toInt(swipe.pageY)
this.bindSwipeMove()
this.bindSwipeEnd()
Events.emit('swipe.start')
}
},
/**
* Handler for `swipemove` event. Calculates user's tap angle and distance.
*
* @param {Object} event
*/
move (event) {
if (!Glide.disabled) {
const { touchAngle, touchRatio, classes } = Glide.settings
const swipe = this.touches(event)
const subExSx = toInt(swipe.pageX) - swipeStartX
const subEySy = toInt(swipe.pageY) - swipeStartY
const powEX = Math.abs(subExSx << 2)
const powEY = Math.abs(subEySy << 2)
const swipeHypotenuse = Math.sqrt(powEX + powEY)
const swipeCathetus = Math.sqrt(powEY)
swipeSin = Math.asin(swipeCathetus / swipeHypotenuse)
if (swipeSin * 180 / Math.PI < touchAngle) {
event.stopPropagation()
Components.Move.make(subExSx * toFloat(touchRatio))
Components.Html.root.classList.add(classes.dragging)
Events.emit('swipe.move')
} else {
return false
}
}
},
/**
* Handler for `swipeend` event. Finitializes user's tap and decides about glide move.
*
* @param {Object} event
* @return {Void}
*/
end (event) {
if (!Glide.disabled) {
const { perSwipe, touchAngle, classes } = Glide.settings
const swipe = this.touches(event)
const threshold = this.threshold(event)
const swipeDistance = swipe.pageX - swipeStartX
const swipeDeg = swipeSin * 180 / Math.PI
this.enable()
if (swipeDistance > threshold && swipeDeg < touchAngle) {
Components.Run.make(Components.Direction.resolve(`${perSwipe}<`))
} else if (swipeDistance < -threshold && swipeDeg < touchAngle) {
Components.Run.make(Components.Direction.resolve(`${perSwipe}>`))
} else {
// While swipe don't reach distance apply previous transform.
Components.Move.make()
}
Components.Html.root.classList.remove(classes.dragging)
this.unbindSwipeMove()
this.unbindSwipeEnd()
Events.emit('swipe.end')
}
},
/**
* Binds swipe's starting event.
*
* @return {Void}
*/
bindSwipeStart () {
const { swipeThreshold, dragThreshold } = Glide.settings
if (swipeThreshold) {
Binder.on(START_EVENTS[0], Components.Html.wrapper, (event) => {
this.start(event)
}, capture)
}
if (dragThreshold) {
Binder.on(START_EVENTS[1], Components.Html.wrapper, (event) => {
this.start(event)
}, capture)
}
},
/**
* Unbinds swipe's starting event.
*
* @return {Void}
*/
unbindSwipeStart () {
Binder.off(START_EVENTS[0], Components.Html.wrapper, capture)
Binder.off(START_EVENTS[1], Components.Html.wrapper, capture)
},
/**
* Binds swipe's moving event.
*
* @return {Void}
*/
bindSwipeMove () {
Binder.on(MOVE_EVENTS, Components.Html.wrapper, throttle((event) => {
this.move(event)
}, Glide.settings.throttle), capture)
},
/**
* Unbinds swipe's moving event.
*
* @return {Void}
*/
unbindSwipeMove () {
Binder.off(MOVE_EVENTS, Components.Html.wrapper, capture)
},
/**
* Binds swipe's ending event.
*
* @return {Void}
*/
bindSwipeEnd () {
Binder.on(END_EVENTS, Components.Html.wrapper, (event) => {
this.end(event)
})
},
/**
* Unbinds swipe's ending event.
*
* @return {Void}
*/
unbindSwipeEnd () {
Binder.off(END_EVENTS, Components.Html.wrapper)
},
/**
* Normalizes event touches points accorting to different types.
*
* @param {Object} event
*/
touches (event) {
if (MOUSE_EVENTS.indexOf(event.type) > -1) {
return event
}
return event.touches[0] || event.changedTouches[0]
},
/**
* Gets value of minimum swipe distance settings based on event type.
*
* @return {Number}
*/
threshold (event) {
const settings = Glide.settings
if (MOUSE_EVENTS.indexOf(event.type) > -1) {
return settings.dragThreshold
}
return settings.swipeThreshold
},
/**
* Enables swipe event.
*
* @return {self}
*/
enable () {
disabled = false
Components.Transition.enable()
return this
},
/**
* Disables swipe event.
*
* @return {self}
*/
disable () {
disabled = true
Components.Transition.disable()
return this
}
}
/**
* Add component class:
* - after initial building
*/
Events.on('build.after', () => {
Components.Html.root.classList.add(Glide.settings.classes.swipeable)
})
/**
* Remove swiping bindings:
* - on destroying, to remove added EventListeners
*/
Events.on('destroy', () => {
Swipe.unbindSwipeStart()
Swipe.unbindSwipeMove()
Swipe.unbindSwipeEnd()
Binder.destroy()
})
return Swipe
}