UNPKG

@better-scroll/core

Version:

Minimalistic core scrolling for BetterScroll, it is pure and tiny

257 lines (222 loc) 5.9 kB
import { TouchEvent, // dom preventDefaultExceptionFn, tagExceptionFn, eventTypeMap, EventType, MouseButton, EventRegister, EventEmitter, } from '@better-scroll/shared-utils' type Exception = { tagName?: RegExp className?: RegExp } export interface Options { [key: string]: boolean | number | Exception click: boolean bindToWrapper: boolean disableMouse: boolean disableTouch: boolean preventDefault: boolean stopPropagation: boolean preventDefaultException: Exception tagException: Exception autoEndDistance: number } export default class ActionsHandler { hooks: EventEmitter initiated: number pointX: number pointY: number wrapperEventRegister: EventRegister targetEventRegister: EventRegister constructor(public wrapper: HTMLElement, public options: Options) { this.hooks = new EventEmitter([ 'beforeStart', 'start', 'move', 'end', 'click', ]) this.handleDOMEvents() } private handleDOMEvents() { const { bindToWrapper, disableMouse, disableTouch, click } = this.options const wrapper = this.wrapper const target = bindToWrapper ? wrapper : window const wrapperEvents = [] const targetEvents = [] const shouldRegisterTouch = !disableTouch const shouldRegisterMouse = !disableMouse if (click) { wrapperEvents.push({ name: 'click', handler: this.click.bind(this), capture: true, }) } if (shouldRegisterTouch) { wrapperEvents.push({ name: 'touchstart', handler: this.start.bind(this), }) targetEvents.push( { name: 'touchmove', handler: this.move.bind(this), }, { name: 'touchend', handler: this.end.bind(this), }, { name: 'touchcancel', handler: this.end.bind(this), } ) } if (shouldRegisterMouse) { wrapperEvents.push({ name: 'mousedown', handler: this.start.bind(this), }) targetEvents.push( { name: 'mousemove', handler: this.move.bind(this), }, { name: 'mouseup', handler: this.end.bind(this), } ) } this.wrapperEventRegister = new EventRegister(wrapper, wrapperEvents) this.targetEventRegister = new EventRegister(target, targetEvents) } private beforeHandler(e: TouchEvent, type: 'start' | 'move' | 'end') { const { preventDefault, stopPropagation, preventDefaultException, } = this.options const preventDefaultConditions = { start: () => { return ( preventDefault && !preventDefaultExceptionFn(e.target, preventDefaultException) ) }, end: () => { return ( preventDefault && !preventDefaultExceptionFn(e.target, preventDefaultException) ) }, move: () => { return preventDefault }, } if (preventDefaultConditions[type]()) { e.preventDefault() } if (stopPropagation) { e.stopPropagation() } } setInitiated(type: number = 0) { this.initiated = type } private start(e: TouchEvent) { const _eventType = eventTypeMap[e.type] if (this.initiated && this.initiated !== _eventType) { return } this.setInitiated(_eventType) // if textarea or other html tags in options.tagException is manipulated // do not make bs scroll if (tagExceptionFn(e.target, this.options.tagException)) { this.setInitiated() return } // only allow mouse left button if (_eventType === EventType.Mouse && e.button !== MouseButton.Left) return if (this.hooks.trigger(this.hooks.eventTypes.beforeStart, e)) { return } this.beforeHandler(e, 'start') let point = (e.touches ? e.touches[0] : e) as Touch this.pointX = point.pageX this.pointY = point.pageY this.hooks.trigger(this.hooks.eventTypes.start, e) } private move(e: TouchEvent) { if (eventTypeMap[e.type] !== this.initiated) { return } this.beforeHandler(e, 'move') let point = (e.touches ? e.touches[0] : e) as Touch let deltaX = point.pageX - this.pointX let deltaY = point.pageY - this.pointY this.pointX = point.pageX this.pointY = point.pageY if ( this.hooks.trigger(this.hooks.eventTypes.move, { deltaX, deltaY, e, }) ) { return } // auto end when out of viewport let scrollLeft = document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop let pX = this.pointX - scrollLeft let pY = this.pointY - scrollTop const autoEndDistance = this.options.autoEndDistance if ( pX > document.documentElement.clientWidth - autoEndDistance || pY > document.documentElement.clientHeight - autoEndDistance || pX < autoEndDistance || pY < autoEndDistance ) { this.end(e) } } private end(e: TouchEvent) { if (eventTypeMap[e.type] !== this.initiated) { return } this.setInitiated() this.beforeHandler(e, 'end') this.hooks.trigger(this.hooks.eventTypes.end, e) } private click(e: TouchEvent) { this.hooks.trigger(this.hooks.eventTypes.click, e) } setContent(content: HTMLElement) { if (content !== this.wrapper) { this.wrapper = content this.rebindDOMEvents() } } rebindDOMEvents() { this.wrapperEventRegister.destroy() this.targetEventRegister.destroy() this.handleDOMEvents() } destroy() { this.wrapperEventRegister.destroy() this.targetEventRegister.destroy() this.hooks.destroy() } }