pull2
Version:
提供了下拉刷新、滚动底部加载组件。原生 js 开发,不依赖任何框架,也可以集成到任何框架。
856 lines (840 loc) • 34.5 kB
JavaScript
'use strict';
var tslib = require('tslib');
// 是否为 window 对象
var isWindow = function (obj) {
return obj !== null && obj !== undefined && obj === (obj === null || obj === void 0 ? void 0 : obj.window);
};
// 是否支持Touch事件
var isSupportTouch = typeof window === 'object' && window && 'ontouchstart' in window;
// 事件
var Events = {
start: isSupportTouch ? 'touchstart' : 'mousedown',
move: isSupportTouch ? 'touchmove' : 'mousemove',
end: isSupportTouch ? 'touchend' : 'mouseup',
cancel: 'touchcancel'
};
// 获取滚动条距离顶部长度
function getScrollTop(el) {
var top = 0;
if (el === document.body || el === document.documentElement) {
top = Math.max(document.body.scrollTop, document.documentElement.scrollTop);
}
else {
top = 'scrollTop' in el ? el.scrollTop : el.scrollY;
}
return top;
}
// 获取事件触发客户端坐标
function getClient(e) {
var x = 0, y = 0;
if (typeof e.clientX === 'number' && typeof e.clientY === 'number') {
x = e.clientX;
y = e.clientY;
}
else if (e.touches && e.touches[0]) {
x = e.touches[0].clientX;
y = e.touches[0].clientY;
}
else if (e.changedTouches && e.changedTouches[0]) {
x = e.changedTouches[0].clientX;
y = e.changedTouches[0].clientY;
}
return {
clientX: x,
clientY: y
};
}
function formatPx(num) {
return typeof num === 'number' ? "".concat(num, "px") : num;
}
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
// 重绘
function reflow(el) {
return el === null || el === void 0 ? void 0 : el.scrollTop;
}
// 获取可视高度
function getClientHeight(el) {
if (typeof window === 'undefined') {
return 0;
}
if (isWindow(el)) {
return document.documentElement.clientHeight;
}
return el.clientHeight;
}
// 获取滚动高度
function getScrollHeight(el) {
if (el === void 0) { el = window; }
if (typeof window === 'undefined') {
return 0;
}
if (isWindow(el)) {
return document.documentElement.scrollHeight;
}
return el.scrollHeight;
}
// 节流
function throttle(fn, wait) {
if (wait === void 0) { wait = 300; }
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-this-alias
var context = this;
var lastCallTime = Date.now();
var hasTimer = false;
var cacheArgs = [];
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
cacheArgs = args;
if (hasTimer)
return;
var now = Date.now();
var diffTime = now - lastCallTime;
if (diffTime >= wait) {
lastCallTime = now;
fn.call(context, cacheArgs);
}
else {
hasTimer = true;
setTimeout(function () {
hasTimer = false;
lastCallTime = Date.now();
fn.call(context, cacheArgs);
}, wait - diffTime);
}
};
}
function processClass(className) {
var cls = [];
if (Array.isArray(className)) {
cls = className;
}
else {
cls = className && typeof className === 'string' ? [className] : [];
}
return cls;
}
var View = /** @class */ (function () {
function View(element) {
this.el = element ? element : document.createElement('div');
this.handler = {};
this.isUnmounted = false; // 标识卸载
this.transitionTimer = null; // 过度样式定时器
}
View.prototype.on = function (type, listener, options) {
if (this.isUnmounted)
return;
if (!this.handler[type]) {
this.handler[type] = [];
}
this.handler[type].push(listener);
this.el.addEventListener(type, listener, options);
};
View.prototype.off = function (type, listener, options) {
if (this.isUnmounted)
return;
this.el.removeEventListener(type, listener, options);
};
View.prototype.setAttrs = function (obj) {
if (this.isUnmounted)
return;
if (obj && typeof obj === 'object') {
for (var prop in obj) {
if (hasOwnProperty(obj, prop)) {
this.el.setAttribute(prop, obj[prop]);
}
}
}
};
View.prototype.setStyle = function (obj) {
if (this.isUnmounted)
return;
if (obj && typeof obj === 'object') {
for (var prop in obj) {
if (hasOwnProperty(obj, prop)) {
// @ts-ignore
this.el.style[prop] = obj[prop];
}
}
}
};
View.prototype.updateTransition = function (enabled, propName) {
if (enabled === void 0) { enabled = false; }
if (propName === void 0) { propName = 'all'; }
var transValue = enabled ? "".concat(propName, " .3s") : '';
this.setStyle({ transition: transValue });
};
View.prototype.html = function (htmlStr) {
if (this.isUnmounted)
return;
this.el.innerHTML = htmlStr;
};
View.prototype.getHeight = function () {
var _a;
return ((_a = this.el) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
};
View.prototype.setHeight = function (num) {
this.setStyle({ height: formatPx(num) });
};
View.prototype.setTransitionHeight = function (num) {
var _this = this;
if (this.isUnmounted)
return;
clearTimeout(this.transitionTimer);
this.updateTransition(true, 'height');
reflow(this.el);
this.setHeight(num);
this.transitionTimer = setTimeout(function () {
_this.updateTransition(false);
}, 300);
};
View.prototype.addClass = function (className) {
if (this.isUnmounted)
return;
var cls = processClass(className);
if (cls.length <= 0) {
return;
}
var prevClasses = this.el.getAttribute('class');
var arrPrevClasses = !prevClasses ? [] : prevClasses.split(' ');
var arrNextClasses = arrPrevClasses
.filter(function (itemCls) { return cls.indexOf(itemCls) === -1; })
.concat(cls);
this.setAttrs({ class: arrNextClasses.join(' ') });
};
View.prototype.removeClass = function (className) {
if (this.isUnmounted)
return;
var cls = processClass(className);
if (cls.length <= 0) {
return;
}
var prevClasses = this.el.getAttribute('class');
var arrPrevClasses = !prevClasses ? [] : prevClasses.split(' ');
var arrNextClasses = arrPrevClasses.filter(function (itemCls) { return cls.indexOf(itemCls) === -1; });
this.setAttrs({ class: arrNextClasses.join(' ') });
};
View.prototype.show = function () {
this.setStyle({ display: 'block' });
};
View.prototype.hide = function () {
this.setStyle({ display: 'none' });
};
View.prototype.destroy = function () {
var _a;
if (this.isUnmounted)
return;
for (var type in this.handler) {
if (this.handler[type].length > 0) {
for (var i = 0; i < this.handler[type].length; i++) {
this.off(type, this.handler[type][i]);
}
this.handler[type].length = 0;
}
delete this.handler[type];
}
(_a = this.el.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this.el);
this.isUnmounted = true;
};
return View;
}());
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z$1 = "@-webkit-keyframes pull2-animate-spin {\n 0% {\n -webkit-transform: rotate(0);\n transform: rotate(0);\n }\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n@keyframes pull2-animate-spin {\n 0% {\n -webkit-transform: rotate(0);\n transform: rotate(0);\n }\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.pull2-icon {\n display: inline-block;\n box-sizing: content-box;\n vertical-align: middle;\n}\n.pull2-icon-spin {\n -webkit-animation: pull2-animate-spin 1s linear infinite;\n animation: pull2-animate-spin 1s linear infinite;\n}\n.pull2-icon-circle {\n position: relative;\n width: 10px;\n height: 10px;\n border: 2px solid #009def;\n border-right-color: transparent;\n border-radius: 50%;\n}\n.pull2-icon-x {\n position: relative;\n width: 14px;\n height: 14px;\n}\n.pull2-icon-x::before,\n.pull2-icon-x::after {\n position: absolute;\n top: 0;\n left: 6px;\n width: 2px;\n height: 14px;\n background: #ee693b;\n -webkit-transform: rotate(45deg);\n transform: rotate(45deg);\n content: '';\n}\n.pull2-icon-x::after {\n -webkit-transform: rotate(135deg);\n transform: rotate(135deg);\n}\n.pull2-icon-check {\n display: inline-block;\n width: 12px;\n height: 6px;\n margin-top: -3px;\n border: 2px solid #13b418;\n border-width: 0 0 2px 2px;\n -webkit-transform: rotate(-45deg);\n transform: rotate(-45deg);\n}\n.pull2-to-refresh {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 40px;\n color: #777;\n font-size: 14px;\n}\n.pull2-to-refresh .pull2-icon {\n margin-right: 5px;\n}\n";
styleInject(css_248z$1);
var _a$1, _b$1;
// 内部状态
var State$1;
(function (State) {
State["Default"] = "default";
State["Drop"] = "drop";
State["Loading"] = "loading";
State["Success"] = "success";
State["Failed"] = "failed";
})(State$1 || (State$1 = {}));
// 默认dom
var defaultDom$1 = (_a$1 = {},
_a$1[State$1.Default] = '<span class="pull2-icon pull2-icon-circle"></span><%=text%>',
_a$1[State$1.Drop] = '<span class="pull2-icon pull2-icon-circle"></span><%=text%>',
_a$1[State$1.Loading] = '<span class="pull2-icon pull2-icon-circle pull2-icon-spin"></span><%=text%>',
_a$1[State$1.Success] = '<span class="pull2-icon pull2-icon-check"></span><%=text%>',
_a$1[State$1.Failed] = '<span class="pull2-icon pull2-icon-x"></span><%=text%>',
_a$1);
// 默认文本
var defaultText$1 = (_b$1 = {},
_b$1[State$1.Default] = '下拉刷新',
_b$1[State$1.Drop] = '释放刷新',
_b$1[State$1.Loading] = '刷新中...',
_b$1[State$1.Success] = '刷新成功',
_b$1[State$1.Failed] = '刷新失败',
_b$1);
var prefixCls$1 = 'pull2-to-refresh';
var RefreshView = /** @class */ (function (_super) {
tslib.__extends(RefreshView, _super);
function RefreshView(options) {
var _this = _super.call(this) || this;
_this.tplMarkText = '<%=text%>';
_this.state = State$1.Default;
_this.options = tslib.__assign(tslib.__assign({ scrollView: document.documentElement }, options), { text: tslib.__assign(tslib.__assign({}, defaultText$1), options === null || options === void 0 ? void 0 : options.text), dom: tslib.__assign(tslib.__assign({}, defaultDom$1), options === null || options === void 0 ? void 0 : options.dom) });
_this.addClass([prefixCls$1, "".concat(prefixCls$1, "-").concat(_this.state)]);
_this.setStyle({ height: '0px', overflow: 'hidden' });
_this.html(_this.getHtml(_this.state));
_this.render();
return _this;
}
RefreshView.prototype.__getWrapper = function (wrapper) {
return isWindow(wrapper) || wrapper === document.documentElement ? document.body : wrapper;
};
RefreshView.prototype.render = function () {
var scrollView = this.options.scrollView;
var wrapper = this.__getWrapper(scrollView);
if (wrapper.firstChild) {
wrapper.insertBefore(this.el, wrapper.firstChild);
}
else {
wrapper.append(this.el);
}
};
RefreshView.prototype.updateOptions = function (options) {
var changedWrapper = false;
if (options.scrollView && options.scrollView !== this.options.scrollView) {
changedWrapper = true;
this.__getWrapper(this.options.scrollView).removeChild(this.el);
}
this.options = tslib.__assign(tslib.__assign(tslib.__assign({}, this.options), options), { text: tslib.__assign(tslib.__assign({}, this.options.text), options === null || options === void 0 ? void 0 : options.text), dom: tslib.__assign(tslib.__assign({}, this.options.dom), options === null || options === void 0 ? void 0 : options.dom) });
this.setState(this.state, true);
if (changedWrapper) {
this.render();
}
};
RefreshView.prototype.getHtml = function (state) {
var _a = this.options, dom = _a.dom, text = _a.text;
return dom[state] ? dom[state].replace(this.tplMarkText, text[state]) : '';
};
RefreshView.prototype.setState = function (state, force) {
if (force === void 0) { force = false; }
if (this.state === state && !force) {
return;
}
if (!hasOwnProperty(this.options.dom, state)) {
throw 'RefreshView 不支持 ' + state + ' 状态';
}
if (this.state !== state) {
this.removeClass("".concat(prefixCls$1, "-").concat(this.state));
this.state = state;
this.addClass("".concat(prefixCls$1, "-").concat(this.state));
}
var htmlStr = this.getHtml(state);
this.html(htmlStr);
};
return RefreshView;
}(View));
var TransitionDurantion = 300; // 动画过渡持续时间
var PullToRefresh = /** @class */ (function () {
function PullToRefresh(options) {
this.options = tslib.__assign({ height: 40, distance: 60, unmovableStayTime: 3000, completionStayTime: 500, scrollView: document.documentElement, isPullDown: function (diffY) { return diffY >= 0; } }, options);
// 如没有设置 onRefresh ,不做任何处理
if (!this.options.onRefresh) {
throw 'PullToRefresh 需要设置 onRefresh 回调方法';
}
this.touchesStart = { x: 0, y: 0 }; // 开始坐标
this.diffY = 0; // 滑动距离
this.isTouch = false; // 标识触摸开始
this.isMove = false; // 标识触摸滑动
this.isLock = false; // 标识锁定
this.isUnmouted = false; // 标识卸载
this.calledLock = false; // 标识是否调用过lock方法
this.lockTimer = null; // 锁定定时器
this.completionStayTimer = null; // 停留定时器
this.__timerFixSlideOutScreen = null; // 定时器,修复滑出屏幕导致下拉刷新显示错误问题
this.handler = {
start: this.fnTouchstart.bind(this),
move: this.fnTouchmove.bind(this),
end: this.fnTouchend.bind(this)
};
this.init();
}
PullToRefresh.prototype.init = function () {
var _a = this.options, dom = _a.dom, text = _a.text, scrollView = _a.scrollView;
this.view = new RefreshView({ dom: dom, text: text, scrollView: scrollView });
this.bindEvent();
};
// 绑定事件
PullToRefresh.prototype.bindEvent = function () {
if (this.isUnmouted)
return;
var scrollView = this.options.scrollView;
scrollView.addEventListener(Events.start, this.handler.start, { passive: false });
};
// 解绑事件
PullToRefresh.prototype.unbindEvent = function () {
if (this.isUnmouted)
return;
var scrollView = this.options.scrollView;
scrollView.removeEventListener(Events.start, this.handler.start);
this.unbindDocumentEvent();
};
PullToRefresh.prototype.bindDocumentEvent = function () {
document.addEventListener(Events.move, this.handler.move, { passive: false });
document.addEventListener(Events.end, this.handler.end, { passive: false });
document.addEventListener(Events.cancel, this.handler.end, { passive: false });
};
PullToRefresh.prototype.unbindDocumentEvent = function () {
document.removeEventListener(Events.move, this.handler.move);
document.removeEventListener(Events.end, this.handler.end);
document.removeEventListener(Events.cancel, this.handler.end);
};
// 开始触摸
PullToRefresh.prototype.fnTouchstart = function (e) {
if (this.view.state !== State$1.Default || this.isLock) {
return;
}
var scrollTop = getScrollTop(this.options.scrollView);
// 滚动区域需置顶
if (scrollTop > 0) {
return;
}
this.bindDocumentEvent();
this.isTouch = true;
this.isMove = false;
this.diffY = 0;
this.view.updateTransition(false);
var _a = getClient(e), clientX = _a.clientX, clientY = _a.clientY;
this.touchesStart.x = clientX;
this.touchesStart.y = clientY;
};
// 触摸移动
PullToRefresh.prototype.fnTouchmove = function (e) {
var _this = this;
if (!this.isTouch) {
return;
}
var _a = getClient(e), clientX = _a.clientX, clientY = _a.clientY;
var diffY = clientY - this.touchesStart.y;
if (!this.isMove) {
var diffX = clientX - this.touchesStart.x;
// 可能没有移动但是触发move事件
if (diffX === 0 && diffY === 0) {
e.preventDefault();
return;
}
// 横向移动或往上滑动,不触发下拉刷新
if (!this.options.isPullDown(diffY)) {
this.isTouch = false;
return;
}
this.isMove = true;
}
e.preventDefault();
var _b = this.options, unmovableStayTime = _b.unmovableStayTime, distance = _b.distance;
this.diffY = diffY;
// 触摸停留2s后,执行touchend事件
clearTimeout(this.__timerFixSlideOutScreen);
this.__timerFixSlideOutScreen = setTimeout(function () {
_this.fnTouchend();
}, unmovableStayTime);
// 下拉dom的偏移高度
var offsetY = 0;
// 下拉
if (this.diffY <= distance) {
offsetY = this.diffY;
this.view.setState(State$1.Default);
// 指定距离 < 下拉距离 < 指定距离*2
}
else if (this.diffY > distance && this.diffY <= distance * 2) {
offsetY = distance + (this.diffY - distance) * 0.5;
this.view.setState(State$1.Drop);
// 下拉距离 > 指定距离*2
}
else {
offsetY = distance + distance * 0.5 + (this.diffY - distance * 2) * 0.2;
}
this.view.setHeight(Math.max(offsetY, 0));
};
// 触摸结束
PullToRefresh.prototype.fnTouchend = function () {
return tslib.__awaiter(this, void 0, void 0, function () {
var _a, distance, completionStayTime, interval;
var _this = this;
return tslib.__generator(this, function (_b) {
switch (_b.label) {
case 0:
clearTimeout(this.__timerFixSlideOutScreen);
this.unbindDocumentEvent();
if (!this.isTouch || !this.isMove) {
this.isTouch = false;
this.isMove = false;
return [2 /*return*/];
}
_a = this.options, distance = _a.distance, completionStayTime = _a.completionStayTime;
this.isTouch = false;
this.isMove = false;
this.internalLock();
interval = TransitionDurantion;
if (!(this.diffY > distance)) return [3 /*break*/, 2];
return [4 /*yield*/, this.triggerRefresh()];
case 1:
_b.sent();
interval += completionStayTime;
return [3 /*break*/, 3];
case 2:
this.view.setTransitionHeight(0);
_b.label = 3;
case 3:
this.lockTimer = setTimeout(function () {
_this.internalUnlock();
}, interval);
return [2 /*return*/];
}
});
});
};
// 重置
PullToRefresh.prototype.resetView = function () {
var _this = this;
this.completionStayTimer = setTimeout(function () {
_this.view.setTransitionHeight(0);
_this.completionStayTimer = setTimeout(function () {
_this.view.setState(State$1.Default);
}, 300);
}, this.options.completionStayTime);
};
// 触发下拉刷新
// 锁定后也支持外部手动触发
PullToRefresh.prototype.triggerRefresh = function () {
var _this = this;
// fix: 如果停留时,手动触发刷新,可能产生异常
if (this.isUnmouted || this.view.state === State$1.Loading)
return;
clearTimeout(this.completionStayTimer);
this.view.setState(State$1.Loading);
this.view.setTransitionHeight(this.options.height);
return this.options
.onRefresh()
.then(function () {
_this.view.setState(State$1.Success);
})
.catch(function () {
_this.view.setState(State$1.Failed);
})
.finally(function () {
_this.resetView();
});
};
// 内部锁定,锁定后将不再触发下拉刷新
PullToRefresh.prototype.internalLock = function () {
clearTimeout(this.lockTimer);
this.isLock = true;
};
// 内部解除锁定
PullToRefresh.prototype.internalUnlock = function () {
clearTimeout(this.lockTimer);
if (!this.calledLock) {
this.isLock = false;
}
};
// 暴露给外部的锁定方法
PullToRefresh.prototype.lock = function () {
this.calledLock = true;
this.internalLock();
};
// 暴露给外部的解除锁定方法
PullToRefresh.prototype.unlock = function () {
this.calledLock = false;
this.internalUnlock();
};
// 更新配置
PullToRefresh.prototype.updateOptions = function (options) {
var changedScrollView = false;
if ((options === null || options === void 0 ? void 0 : options.scrollView) && options.scrollView !== this.options.scrollView) {
changedScrollView = true;
this.unbindEvent();
}
this.options = tslib.__assign(tslib.__assign({}, this.options), options);
var _a = this.options, text = _a.text, dom = _a.dom, scrollView = _a.scrollView;
this.view.updateOptions({ text: text, dom: dom, scrollView: scrollView });
if (changedScrollView) {
this.bindEvent();
}
};
// 销毁
PullToRefresh.prototype.destroy = function () {
if (this.isUnmouted)
return;
this.internalLock();
this.unbindEvent();
this.view.destroy();
this.isUnmouted = true;
};
// 恢复
PullToRefresh.prototype.resume = function () {
if (!this.isUnmouted)
return;
this.isUnmouted = false;
this.init();
this.internalUnlock();
};
return PullToRefresh;
}());
var css_248z = ".pull2-load-more {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 44px;\n color: #777;\n font-size: 14px;\n}\n";
styleInject(css_248z);
var _a, _b;
// 内部状态
var State;
(function (State) {
State["Default"] = "default";
State["Loading"] = "loading";
State["Failed"] = "failed";
State["Done"] = "done";
})(State || (State = {}));
// 默认dom
var defaultDom = (_a = {},
_a[State.Default] = '<%=text%>',
_a[State.Loading] = '<%=text%>',
_a[State.Failed] = '<%=text%>',
_a[State.Done] = '<%=text%>',
_a);
// 默认文本
var defaultText = (_b = {},
_b[State.Default] = '上拉或点击加载更多',
_b[State.Loading] = '正在加载',
_b[State.Failed] = '加载失败,点击重试',
_b[State.Done] = '已全部加载',
_b);
var prefixCls = 'pull2-load-more';
var LoadMoreView = /** @class */ (function (_super) {
tslib.__extends(LoadMoreView, _super);
function LoadMoreView(options) {
var _this = _super.call(this) || this;
_this.tplMarkText = '<%=text%>';
_this.state = State.Default;
_this.options = tslib.__assign(tslib.__assign({ scrollView: document.documentElement }, options), { text: tslib.__assign(tslib.__assign({}, defaultText), options === null || options === void 0 ? void 0 : options.text), dom: tslib.__assign(tslib.__assign({}, defaultDom), options === null || options === void 0 ? void 0 : options.dom) });
_this.addClass([prefixCls, "".concat(prefixCls, "-").concat(_this.state)]);
_this.html(_this.getHtml(_this.state));
_this.render();
return _this;
}
LoadMoreView.prototype.__getWrapper = function (wrapper) {
return isWindow(wrapper) || wrapper === document.documentElement
? document.body
: wrapper;
};
LoadMoreView.prototype.render = function () {
var scrollView = this.options.scrollView;
var wrapper = this.__getWrapper(scrollView);
wrapper.appendChild(this.el);
};
LoadMoreView.prototype.updateOptions = function (options) {
var changedWrapper = false;
if (options.scrollView && options.scrollView !== this.options.scrollView) {
changedWrapper = true;
this.__getWrapper(this.options.scrollView).removeChild(this.el);
}
this.options = tslib.__assign(tslib.__assign(tslib.__assign({}, this.options), options), { text: tslib.__assign(tslib.__assign({}, this.options.text), options === null || options === void 0 ? void 0 : options.text), dom: tslib.__assign(tslib.__assign({}, this.options.dom), options === null || options === void 0 ? void 0 : options.dom) });
this.setState(this.state, true);
if (changedWrapper) {
this.render();
}
};
LoadMoreView.prototype.getHtml = function (state) {
var _a = this.options, dom = _a.dom, text = _a.text;
return dom[state] ? dom[state].replace(this.tplMarkText, text[state]) : '';
};
LoadMoreView.prototype.setState = function (state, force) {
if (force === void 0) { force = false; }
if (this.state === state && !force) {
return;
}
if (!hasOwnProperty(this.options.dom, state)) {
throw 'RefreshView 不支持 ' + state + ' 状态';
}
if (this.state !== state) {
this.removeClass("".concat(prefixCls, "-").concat(this.state));
this.state = state;
this.addClass("".concat(prefixCls, "-").concat(this.state));
}
var htmlStr = this.getHtml(state);
this.html(htmlStr);
};
return LoadMoreView;
}(View));
var ScrollToLoadMore = /** @class */ (function () {
function ScrollToLoadMore(options) {
this.options = tslib.__assign({ threshold: 100, scrollView: document.documentElement, throttleWaitTime: 50, isNoMore: function () { return false; }, autoCheckOnContentUpdate: true }, options);
// 如没有设置 onScrollLower ,不做任何处理
if (!this.options.onScrollLower) {
throw 'ScrollToLoadMore 需要设置 onScrollLower 回调方法';
}
this.isUnmouted = false;
this.isLock = false; // 标识锁定
this.calledLock = false; // 标识外部调用lock方法
this.handler = {
click: this.clickView.bind(this),
scroll: throttle.call(this, this.scroll, this.options.throttleWaitTime > 0 ? this.options.throttleWaitTime : 0)
};
this.init();
}
ScrollToLoadMore.prototype.init = function () {
var _a = this.options, text = _a.text, dom = _a.dom, scrollView = _a.scrollView;
this.view = new LoadMoreView({ text: text, dom: dom, scrollView: scrollView });
this.bindEvent();
};
// 点击
ScrollToLoadMore.prototype.clickView = function () {
if (this.isLock)
return;
if (this.view.state === State.Default || this.view.state === State.Failed) {
this.triggerLoad();
}
};
// 滚动
ScrollToLoadMore.prototype.scroll = function () {
if (this.isLock || this.isUnmouted || this.view.state !== State.Default)
return;
this.checkLoad();
};
// 手动检测是否满足加载下一页条件,一般在页面刷新后调用
ScrollToLoadMore.prototype.checkLoad = function () {
var _a = this.options, scrollView = _a.scrollView, threshold = _a.threshold;
var height = getClientHeight(scrollView); // 容器高度
var scrollHeight = getScrollHeight(scrollView); // 内容高度
var scrollTop = getScrollTop(scrollView);
if (height === 0 && scrollHeight === 0) {
return;
}
if (scrollHeight - (threshold > 0 ? threshold : 0) <= height + scrollTop) {
this.triggerLoad();
}
};
ScrollToLoadMore.prototype.bindEvent = function () {
var scrollView = this.options.scrollView;
this.view.on('click', this.handler.click);
var scrollWrapper = scrollView;
if (scrollView === document.documentElement) {
scrollWrapper = window;
}
scrollWrapper.addEventListener('scroll', this.handler.scroll);
};
ScrollToLoadMore.prototype.unbindEvent = function () {
var scrollView = this.options.scrollView;
this.view.off('click', this.handler.click);
var scrollWrapper = scrollView;
if (scrollView === document.documentElement) {
scrollWrapper = window;
}
scrollWrapper.removeEventListener('scroll', this.handler.scroll);
};
ScrollToLoadMore.prototype.triggerLoad = function () {
var _this = this;
// 避免手动调用重复触发
if (this.isUnmouted || this.view.state === State.Loading)
return;
this.internalLock();
this.view.setState(State.Loading);
var _a = this.options, onScrollLower = _a.onScrollLower, isNoMore = _a.isNoMore, autoCheckOnContentUpdate = _a.autoCheckOnContentUpdate;
return onScrollLower()
.then(function (res) {
_this.internalUnlock();
if (isNoMore(res)) {
_this.view.setState(State.Done);
}
else {
_this.view.setState(State.Default);
// 触发更新内容高度,滚动容器高度。
// 可以由外部触发。比如外部下拉刷新或者内容增删改,需要重新更新内容高度。
if (autoCheckOnContentUpdate) {
_this.checkLoad();
}
}
return res;
})
.catch(function () {
_this.view.setState(State.Failed);
_this.internalUnlock();
});
};
// 重置状态
ScrollToLoadMore.prototype.reset = function () {
this.view.setState(State.Default);
};
ScrollToLoadMore.prototype.updateOptions = function (options) {
var changedScrollView = false;
if ((options === null || options === void 0 ? void 0 : options.scrollView) && options.scrollView !== this.options.scrollView) {
changedScrollView = true;
this.unbindEvent();
}
if (typeof (options === null || options === void 0 ? void 0 : options.throttleWaitTime) === 'number' &&
options.throttleWaitTime !== this.options.throttleWaitTime) {
this.handler.scroll = throttle.call(this, this.scroll, options.throttleWaitTime > 0 ? options.throttleWaitTime : 0);
}
this.options = tslib.__assign(tslib.__assign({}, this.options), options);
var _a = this.options, text = _a.text, dom = _a.dom, scrollView = _a.scrollView;
this.view.updateOptions({ text: text, dom: dom, scrollView: scrollView });
if (changedScrollView) {
this.bindEvent();
}
};
// 内部锁定,锁定后将不再触发滚动底部加载
ScrollToLoadMore.prototype.internalLock = function () {
this.isLock = true;
};
// 内部解除锁定
ScrollToLoadMore.prototype.internalUnlock = function () {
if (!this.calledLock) {
this.isLock = false;
}
};
// 暴露给外部的锁定方法
ScrollToLoadMore.prototype.lock = function () {
this.calledLock = true;
this.internalLock();
};
// 暴露给外部的解除锁定方法
ScrollToLoadMore.prototype.unlock = function () {
this.calledLock = false;
this.internalUnlock();
};
ScrollToLoadMore.prototype.destroy = function () {
if (this.isUnmouted)
return;
this.internalLock();
this.unbindEvent();
this.view.destroy();
this.isUnmouted = true;
};
// 恢复
ScrollToLoadMore.prototype.resume = function () {
if (!this.isUnmouted)
return;
this.isUnmouted = false;
this.init();
this.internalUnlock();
};
return ScrollToLoadMore;
}());
exports.PullToRefresh = PullToRefresh;
exports.ScrollToLoadMore = ScrollToLoadMore;