UNPKG

uikit

Version:

UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.

216 lines (142 loc) 5.71 kB
import {closest, css, getEventPos, includes, isRtl, isTouch, off, on, pointerCancel, pointerDown, pointerMove, pointerUp, selInput, trigger} from 'uikit-util'; export default { props: { draggable: Boolean }, data: { draggable: true, threshold: 10 }, created() { ['start', 'move', 'end'].forEach(key => { const fn = this[key]; this[key] = e => { const pos = getEventPos(e).x * (isRtl ? -1 : 1); this.prevPos = pos !== this.pos ? this.pos : this.prevPos; this.pos = pos; fn(e); }; }); }, events: [ { name: pointerDown, delegate() { return this.selSlides; }, handler(e) { if (!this.draggable || !isTouch(e) && hasTextNodesOnly(e.target) || closest(e.target, selInput) || e.button > 0 || this.length < 2 ) { return; } this.start(e); } }, { name: 'dragstart', handler(e) { e.preventDefault(); } } ], methods: { start() { this.drag = this.pos; if (this._transitioner) { this.percent = this._transitioner.percent(); this.drag += this._transitioner.getDistance() * this.percent * this.dir; this._transitioner.cancel(); this._transitioner.translate(this.percent); this.dragging = true; this.stack = []; } else { this.prevIndex = this.index; } on(document, pointerMove, this.move, {passive: false}); // 'input' event is triggered by video controls on(document, `${pointerUp} ${pointerCancel} input`, this.end, true); css(this.list, 'userSelect', 'none'); }, move(e) { const distance = this.pos - this.drag; if (distance === 0 || this.prevPos === this.pos || !this.dragging && Math.abs(distance) < this.threshold) { return; } // prevent click event css(this.list, 'pointerEvents', 'none'); e.cancelable && e.preventDefault(); this.dragging = true; this.dir = (distance < 0 ? 1 : -1); const {slides} = this; let {prevIndex} = this; let dis = Math.abs(distance); let nextIndex = this.getIndex(prevIndex + this.dir, prevIndex); let width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth; while (nextIndex !== prevIndex && dis > width) { this.drag -= width * this.dir; prevIndex = nextIndex; dis -= width; nextIndex = this.getIndex(prevIndex + this.dir, prevIndex); width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth; } this.percent = dis / width; const prev = slides[prevIndex]; const next = slides[nextIndex]; const changed = this.index !== nextIndex; const edge = prevIndex === nextIndex; let itemShown; [this.index, this.prevIndex].filter(i => !includes([nextIndex, prevIndex], i)).forEach(i => { trigger(slides[i], 'itemhidden', [this]); if (edge) { itemShown = true; this.prevIndex = prevIndex; } }); if (this.index === prevIndex && this.prevIndex !== prevIndex || itemShown) { trigger(slides[this.index], 'itemshown', [this]); } if (changed) { this.prevIndex = prevIndex; this.index = nextIndex; !edge && trigger(prev, 'beforeitemhide', [this]); trigger(next, 'beforeitemshow', [this]); } this._transitioner = this._translate(Math.abs(this.percent), prev, !edge && next); if (changed) { !edge && trigger(prev, 'itemhide', [this]); trigger(next, 'itemshow', [this]); } }, end() { off(document, pointerMove, this.move, {passive: false}); off(document, `${pointerUp} ${pointerCancel} input`, this.end, true); if (this.dragging) { this.dragging = null; if (this.index === this.prevIndex) { this.percent = 1 - this.percent; this.dir *= -1; this._show(false, this.index, true); this._transitioner = null; } else { const dirChange = (isRtl ? this.dir * (isRtl ? 1 : -1) : this.dir) < 0 === this.prevPos > this.pos; this.index = dirChange ? this.index : this.prevIndex; if (dirChange) { this.percent = 1 - this.percent; } this.show(this.dir > 0 && !dirChange || this.dir < 0 && dirChange ? 'next' : 'previous', true); } } css(this.list, {userSelect: '', pointerEvents: ''}); this.drag = this.percent = null; } } }; function hasTextNodesOnly(el) { return !el.children.length && el.childNodes.length; }