press-ui
Version:
简单、易用的跨端组件库,兼容 Vue2 和 Vue3,同时支持 uni-app和普通 Vue 项目
260 lines (252 loc) • 7.99 kB
JavaScript
import { Scroll } from './scroll';
function i(scroll, t, n) {
function i(t, scroll, r, o) {
if (!t || !t.cancelled) {
r(scroll);
const a = scroll.done();
if (!a) {
if (!t.cancelled) {
t.id = requestAnimationFrame(i.bind(null, t, scroll, r, o));
}
}
if (a && o) {
o(scroll);
}
}
}
function r(scroll) {
if (scroll && scroll.id) {
cancelAnimationFrame(scroll.id);
}
if (scroll) {
scroll.cancelled = true;
}
}
const o = {
id: 0,
cancelled: false,
};
i(o, scroll, t, n);
return {
cancel: r.bind(null, o),
model: scroll,
};
}
export function Scroller(element, options) {
options = options || {};
this._element = element;
this._options = options;
this._enableSnap = options.enableSnap || false;
this._itemSize = options.itemSize || 0;
this._enableX = options.enableX || false;
this._enableY = options.enableY || false;
this._shouldDispatchScrollEvent = !!options.onScroll;
if (this._enableX) {
this._extent = (options.scrollWidth || this._element.offsetWidth) - this._element.parentElement.offsetWidth;
this._scrollWidth = options.scrollWidth;
} else {
this._extent = (options.scrollHeight || this._element.offsetHeight) - this._element.parentElement.offsetHeight;
this._scrollHeight = options.scrollHeight;
}
this._position = 0;
this._scroll = new Scroll(this._extent, options.friction, options.spring);
this._onTransitionEnd = this.onTransitionEnd.bind(this);
this.updatePosition();
}
Scroller.prototype.onTouchStart = function () {
this._startPosition = this._position;
this._lastChangePos = this._startPosition;
if (this._startPosition > 0) {
this._startPosition /= 0.5;
} else {
if (this._startPosition < -this._extent) {
this._startPosition = (this._startPosition + this._extent) / 0.5 - this._extent;
}
}
if (this._animation) {
this._animation.cancel();
this._scrolling = false;
}
this.updatePosition();
};
Scroller.prototype.onTouchMove = function (x, y) {
let startPosition = this._startPosition;
if (this._enableX) {
startPosition += x;
} else if (this._enableY) {
startPosition += y;
}
if (startPosition > 0) {
startPosition *= 0.5;
} else if (startPosition < -this._extent) {
startPosition = 0.5 * (startPosition + this._extent) - this._extent;
}
this._position = startPosition;
this.updatePosition();
this.dispatchScroll();
};
Scroller.prototype.onTouchEnd = function (e, r, o) {
if (this._enableSnap && this._position > -this._extent && this._position < 0) {
if (this._enableY && ((Math.abs(r) < this._itemSize && Math.abs(o.y) < 300) || Math.abs(o.y) < 150)) {
this.snap();
return;
}
if (this._enableX && ((Math.abs(e) < this._itemSize && Math.abs(o.x) < 300) || Math.abs(o.x) < 150)) {
this.snap();
return;
}
}
if (this._enableX) {
this._scroll.set(this._position, o.x);
} else if (this._enableY) {
this._scroll.set(this._position, o.y);
}
let c;
if (this._enableSnap) {
const s = this._scroll._friction.x(100);
const l = s % this._itemSize;
c = Math.abs(l) > this._itemSize / 2 ? s - (this._itemSize - Math.abs(l)) : s - l;
if (c <= 0 && c >= -this._extent) {
this._scroll.setVelocityByEnd(c);
}
}
this._lastTime = Date.now();
this._lastDelay = 0;
this._scrolling = true;
this._lastChangePos = this._position;
this._lastIdx = Math.floor(Math.abs(this._position / this._itemSize));
this._animation = i(this._scroll, () => {
const e = Date.now();
const i = (e - this._scroll._startTime) / 1e3;
const r = this._scroll.x(i);
this._position = r;
this.updatePosition();
const o = this._scroll.dx(i);
if (this._shouldDispatchScrollEvent && e - this._lastTime > this._lastDelay) {
this.dispatchScroll();
this._lastDelay = Math.abs(2e3 / o);
this._lastTime = e;
}
}, () => {
if (this._enableSnap) {
if (c <= 0 && c >= -this._extent) {
this._position = c;
this.updatePosition();
}
if (typeof this._options.onSnap === 'function') {
this._options.onSnap(Math.floor(Math.abs(this._position) / this._itemSize));
}
}
if (this._shouldDispatchScrollEvent) {
this.dispatchScroll();
}
this._scrolling = false;
});
};
Scroller.prototype.onTransitionEnd = function () {
this._element.style.transition = '';
this._element.style.webkitTransition = '';
this._element.removeEventListener('transitionend', this._onTransitionEnd);
this._element.removeEventListener('webkitTransitionEnd', this._onTransitionEnd);
if (this._snapping) {
this._snapping = false;
}
this.dispatchScroll();
};
Scroller.prototype.snap = function () {
const e = this._itemSize;
const t = this._position % e;
const i = Math.abs(t) > this._itemSize / 2 ? this._position - (e - Math.abs(t)) : this._position - t;
if (this._position !== i) {
this._snapping = true;
this.scrollTo(-i);
if (typeof this._options.onSnap === 'function') {
this._options.onSnap(Math.floor(Math.abs(this._position) / this._itemSize));
}
}
};
Scroller.prototype.scrollTo = function (e, t) {
if (this._animation) {
this._animation.cancel();
this._scrolling = false;
}
if (typeof e === 'number') {
this._position = -e;
}
if (this._position < -this._extent) {
this._position = -this._extent;
} else {
if (this._position > 0) {
this._position = 0;
}
}
this._element.style.transition = `transform ${t || 0.2}s ease-out`;
this._element.style.webkitTransition = `-webkit-transform ${t || 0.2}s ease-out`;
this.updatePosition();
this._element.addEventListener('transitionend', this._onTransitionEnd);
this._element.addEventListener('webkitTransitionEnd', this._onTransitionEnd);
};
Scroller.prototype.dispatchScroll = function () {
if (typeof this._options.onScroll === 'function' && Math.round(this._lastPos) !== Math.round(this._position)) {
this._lastPos = this._position;
const e = {
target: {
scrollLeft: this._enableX ? -this._position : 0,
scrollTop: this._enableY ? -this._position : 0,
scrollHeight: this._scrollHeight || this._element.offsetHeight,
scrollWidth: this._scrollWidth || this._element.offsetWidth,
offsetHeight: this._element.parentElement.offsetHeight,
offsetWidth: this._element.parentElement.offsetWidth,
},
};
this._options.onScroll(e);
}
};
Scroller.prototype.update = function (e, t, n) {
let i = 0;
const r = this._position;
if (this._enableX) {
i = this._element.childNodes.length
? (t || this._element.offsetWidth) - this._element.parentElement.offsetWidth : 0;
this._scrollWidth = t;
} else {
i = this._element.childNodes.length
? (t || this._element.offsetHeight) - this._element.parentElement.offsetHeight : 0;
this._scrollHeight = t;
}
if (typeof e === 'number') {
this._position = -e;
}
if (this._position < -i) {
this._position = -i;
} else {
if (this._position > 0) {
this._position = 0;
}
}
this._itemSize = n || this._itemSize;
this.updatePosition();
if (r !== this._position) {
this.dispatchScroll();
if (typeof this._options.onSnap === 'function') {
this._options.onSnap(Math.floor(Math.abs(this._position) / this._itemSize));
}
}
this._extent = i;
this._scroll._extent = i;
};
Scroller.prototype.updatePosition = function () {
let transform = '';
if (this._enableX) {
transform = `translateX(${this._position}px) translateZ(0)`;
} else {
if (this._enableY) {
transform = `translateY(${this._position}px) translateZ(0)`;
}
}
this._element.style.webkitTransform = transform;
this._element.style.transform = transform;
};
Scroller.prototype.isScrolling = function () {
return this._scrolling || this._snapping;
};