2025-04-14 10:57:27 +08:00

383 lines
13 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// [z-paging]微信小程序、QQ小程序、app-vue、h5上使用wxs实现自定义下拉刷新降低逻辑层与视图层的通信折损提升性能
var currentDis = 0;
var isPCFlag = -1;
var startY = -1;
// 监听js层传过来的数据
function propObserver(newVal, oldVal, ownerIns, ins) {
var state = ownerIns.getState() || {};
state.currentIns = ins;
var dataset = ins.getDataset();
var loading = dataset.loading == true;
// 如果是下拉刷新结束更新transform
if (newVal && newVal.indexOf('end') != -1) {
var transition = newVal.split('end')[0];
_setTransform('translateY(0px)', ins, false, transition);
state.moveDis = 0;
state.oldMoveDis = 0;
currentDis = 0;
} else if (newVal && newVal.indexOf('begin') != -1) {
// 如果是下拉刷新开始更新transform
var refresherThreshold = ins.getDataset().refresherthreshold;
_setTransformValue(refresherThreshold, ins, state, false);
}
}
// touch开始
function touchstart(e, ownerIns) {
var ins = _getIns(ownerIns);
var state = {};
var dataset = {};
ownerIns.callMethod('_handleListTouchstart');
if (ins) {
state = ins.getState();
dataset = ins.getDataset();
if (_touchDisabled(e, ins, 0)) return;
}
var isTouchEnded = state.isTouchEnded;
state.oldMoveDis = 0;
var touch = _getTouch(e);
var loading = _isTrue(dataset.loading);
state.startY = touch.touchY;
startY = state.startY;
state.lastTouch = touch;
if (!loading && isTouchEnded) {
state.isTouchmoving = false;
}
state.isTouchEnded = false;
// 通知js层touch开始
ownerIns.callMethod('_handleRefresherTouchstart', touch);
}
// touch中
function touchmove(e, ownerIns) {
var touch = _getTouch(e);
var ins = _getIns(ownerIns);
var dataset = ins.getDataset();
var refresherThreshold = dataset.refresherthreshold;
var refresherF2Threshold = dataset.refresherf2threshold;
var refresherF2Enabled = _isTrue(dataset.refresherf2enabled);
var isIos = _isTrue(dataset.isios);
var state = ins.getState();
var watchTouchDirectionChange = _isTrue(dataset.watchtouchdirectionchange);
var moveDisObj = {};
var moveDis = 0;
var prevent = false;
// 如果需要监听touch方向的改变
if (watchTouchDirectionChange) {
moveDisObj = _getMoveDis(e, ins);
moveDis = moveDisObj.currentDis;
prevent = moveDisObj.isDown;
var direction = prevent ? 'top' : 'bottom';
// 确保只在touch方向改变时通知一次js层而不是touchmove中持续通知
if (prevent == state.oldTouchDirection && prevent != state.oldEmitedTouchDirection) {
ownerIns.callMethod('_handleTouchDirectionChange', { direction: direction });
state.oldEmitedTouchDirection = prevent;
}
state.oldTouchDirection = prevent;
}
// 判断是否允许下拉刷新
if (_touchDisabled(e, ins, 1)) {
_handlePullingDown(state, ownerIns, false);
return true;
}
// 判断下拉刷新的角度是否在要求范围内
if (!_getAngleIsInRange(e, touch, state, dataset)) {
_handlePullingDown(state, ownerIns, false);
return true;
}
moveDisObj = _getMoveDis(e, ins);
moveDis = moveDisObj.currentDis;
prevent = moveDisObj.isDown;
if (moveDis < 0) {
// moveDis0transform0
_setTransformValue(0, ins, state, false);
_handlePullingDown(state, ownerIns, false);
return true;
}
if (prevent && !state.disabledBounce) {
// js
ownerIns.callMethod('_handleScrollViewBounce', { bounce: false });
state.disabledBounce = true;
_handlePullingDown(state, ownerIns, prevent);
return !prevent;
}
// transform
_setTransformValue(moveDis, ins, state, false);
var oldRefresherStatus = state.refresherStatus;
var oldIsTouchmoving = _isTrue(dataset.oldistouchmoving);
var hasTouchmove = _isTrue(dataset.hastouchmove);
var isTouchmoving = state.isTouchmoving;
state.refresherStatus = moveDis >= refresherThreshold ? (refresherF2Enabled && moveDis > refresherF2Threshold ? 'goF2' : 'releaseToRefresh') : 'default';
if (!isTouchmoving) {
state.isTouchmoving = true;
isTouchmoving = true;
}
if (state.isTouchEnded) {
state.isTouchEnded = false;
}
// 如果需要实时监听下拉位置偏移则需要实时通知js层此操作会使wxs层与js层频繁通信从而导致在一些性能较差设备中下拉刷新卡顿
if (hasTouchmove) {
ownerIns.callMethod('_handleWxsPullingDown', { moveDis: moveDis, diffDis: moveDisObj.diffDis });
}
// 在下拉刷新状态改变时通知js层
if (oldRefresherStatus == undefined || oldRefresherStatus != state.refresherStatus || oldIsTouchmoving != isTouchmoving) {
ownerIns.callMethod('_handleRefresherTouchmove', moveDis, touch);
}
_handlePullingDown(state, ownerIns, prevent);
return !prevent;
}
// touch结束
function touchend(e, ownerIns) {
var touch = _getTouch(e);
var ins = _getIns(ownerIns);
var dataset = ins.getDataset();
var state = ins.getState();
if (state.disabledBounce) {
// 通知js允许列表滚动
ownerIns.callMethod('_handleScrollViewBounce', { bounce: true });
state.disabledBounce = false;
}
if (_touchDisabled(e, ins, 2)) return;
state.reachMaxAngle = true;
state.hitReachMaxAngleCount = 0;
state.fixedIsTopHitCount = 0;
if (!state.isTouchmoving) return;
var oldRefresherStatus = state.refresherStatus;
var oldMoveDis = state.moveDis;
var refresherThreshold = ins.getDataset().refresherthreshold;
var moveDis = _getMoveDis(e, ins).currentDis;
if (!(moveDis >= refresherThreshold && oldRefresherStatus === 'releaseToRefresh')) {
state.isTouchmoving = false;
}
// 通知js层touch结束
ownerIns.callMethod('_handleRefresherTouchend', moveDis);
state.isTouchEnded = true;
if (oldMoveDis < refresherThreshold) return;
var animate = false;
if (moveDis >= refresherThreshold) {
moveDis = refresherThreshold;
animate = true;
}
_setTransformValue(moveDis, ins, state, animate);
}
// #ifdef H5
// 判断是否是pc平台
function isPC() {
if (!navigator) return false;
if (isPCFlag != -1) return isPCFlag;
var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
isPCFlag = agents.every(function(item) { return navigator.userAgent.indexOf(item) < 0 });
return isPCFlag;
}
var movable = false;
// pcmousedownmousemovemouseuptouch使pc
function mousedown(e, ins) {
if (!isPC()) return;
touchstart(e, ins);
movable = true;
}
function mousemove(e, ins) {
if (!isPC() || !movable) return;
touchmove(e, ins);
}
function mouseup(e, ins) {
if (!isPC()) return;
touchend(e, ins);
movable = false;
}
function mouseleave(e, ins) {
if (!isPC()) return;
movable = false;
}
// #endif
// transform
function _setTransformValue(value, ins, state, animate) {
value = value || 0;
if (state.moveDis == value) return;
state.moveDis = value;
_setTransform('translateY(' + value + 'px)', ins, animate, '');
}
// transform使js
function _setTransform(transform, ins, animate, transition) {
var dataset = ins.getDataset();
if (_isTrue(dataset.refreshernotransform)) return;
transform = transform == 'translateY(0px)' ? 'none' : transform;
ins.requestAnimationFrame(function() {
var stl = { 'transform': transform };
if (animate) {
stl['transition'] = 'transform .1s linear';
}
if (transition.length) {
stl['transition'] = transition;
}
ins.setStyle(stl);
})
}
//
function _getMoveDis(e, ins) {
var state = ins.getState();
var refresherThreshold = parseFloat(ins.getDataset().refresherthreshold);
var refresherOutRate = parseFloat(ins.getDataset().refresheroutrate);
var refresherPullRate = parseFloat(ins.getDataset().refresherpullrate);
var touch = _getTouch(e);
var currentStartY = !state.startY || state.startY == 'NaN' ? startY : state.startY;
var moveDis = touch.touchY - currentStartY;
var oldMoveDis = state.oldMoveDis || 0;
state.oldMoveDis = moveDis;
//
var diffDis = moveDis - oldMoveDis;
if (diffDis > 0) {
// 对偏移量进行进一步处理通过refresherPullRate等配置进行约束
diffDis = diffDis * refresherPullRate;
if (currentDis > refresherThreshold) {
diffDis = diffDis * (1 - refresherOutRate);
}
}
// 控制diffDis过大的情况比如进入页面突然猛然下拉此时diffDis不应进行太大的偏移
diffDis = diffDis > 100 ? diffDis / 100 : (diffDis > 20 ? diffDis / 2.2 : diffDis);
currentDis += diffDis;
currentDis = Math.max(0, currentDis);
return {
currentDis: currentDis,
diffDis: diffDis,
isDown: diffDis > 0
};
}
// 获取经过统一格式包装的当前touch对象
function _getTouch(e) {
var touch = e;
if (e.touches && e.touches.length) {
touch = e.touches[0];
} else if (e.changedTouches && e.changedTouches.length) {
touch = e.changedTouches[0];
} else if (e.datail && e.datail != {}) {
touch = e.datail;
}
return {
touchX: touch.clientX,
touchY: touch.clientY
};
}
// 获取当前currentIns
function _getIns(ownerIns) {
var ins = ownerIns.getState().currentIns;
if (!ins) {
ownerIns.callMethod('_handlePropUpdate');
}
return ins;
}
// 判断当前状态是否允许下拉刷新
function _touchDisabled(e, ins, processTag) {
var dataset = ins.getDataset();
var state = ins.getState();
var loading = _isTrue(dataset.loading);
var useChatRecordMode = _isTrue(dataset.usechatrecordmode);
var refresherEnabled = _isTrue(dataset.refresherenabled);
var useCustomRefresher = _isTrue(dataset.usecustomrefresher);
var usePageScroll = _isTrue(dataset.usepagescroll);
var pageScrollTop = parseFloat(dataset.pagescrolltop);
var scrollTop = parseFloat(dataset.scrolltop);
var finalScrollTop = usePageScroll ? pageScrollTop : scrollTop;
var fixedIsTop = false;
// 是否要处理滚动到顶部scrollTop不为0时候的容错为解决在安卓中scroll-view有概率滚动到顶部时scrollTop不为0导致下拉刷新判断异常但此方案会导致某些情况例如滚动到距离顶部10px处下拉抖动因此改为通过获取zp-scroll-view的节点信息中的scrollTop进行验证的方案
var handleFaultTolerantMove = false;
if (handleFaultTolerantMove && finalScrollTop == (state.startScrollTop || 0) && finalScrollTop <= 105) {
fixedIsTop = true;
}
var fixedIsTopHitCount = state.fixedIsTopHitCount || 0;
if (fixedIsTop) {
fixedIsTopHitCount ++;
if (fixedIsTopHitCount <= 2) {
fixedIsTop = false;
}
state.fixedIsTopHitCount = fixedIsTopHitCount;
} else {
state.fixedIsTopHitCount = 0;
}
if (handleFaultTolerantMove && processTag === 0) {
state.startScrollTop = finalScrollTop || 0;
}
if (handleFaultTolerantMove && processTag === 2) {
fixedIsTop = true;
}
return loading || useChatRecordMode || !refresherEnabled || !useCustomRefresher ||
((usePageScroll && useCustomRefresher && pageScrollTop > 5) && !fixedIsTop) ||
((!usePageScroll && useCustomRefresher && scrollTop > 5) && !fixedIsTop);
}
// 判断下拉刷新的角度是否在要求范围内
function _getAngleIsInRange(e, touch, state, dataset) {
var maxAngle = dataset.refreshermaxangle;
var refresherAecc = _isTrue(dataset.refresheraecc);
var lastTouch = state.lastTouch;
var reachMaxAngle = state.reachMaxAngle;
var moveDis = state.oldMoveDis;
if (!lastTouch) return true;
if (maxAngle >= 0 && maxAngle <= 90 && lastTouch) {
// 考虑下拉刷新手势由水平移动转为垂直方向移动的情况此时不应当只判断垂直方向角度是否符合要求应当直接禁止以避免在swiper中使用下拉刷新时横向切换swiper途中手未离开屏幕还可以下拉刷新的问题
if ((!moveDis || moveDis < 1) && !refresherAecc && reachMaxAngle != null && !reachMaxAngle) return false;
var x = Math.abs(touch.touchX - lastTouch.touchX);
var y = Math.abs(touch.touchY - lastTouch.touchY);
var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if ((x || y) && x > 1) {
// 获取下拉刷新前后两次位移的角度
var angle = Math.asin(y / z) / Math.PI * 180;
if (angle < maxAngle) {
// returnhitReachMaxAngleCount使
var hitReachMaxAngleCount = state.hitReachMaxAngleCount || 0;
state.hitReachMaxAngleCount = ++hitReachMaxAngleCount;
if (state.hitReachMaxAngleCount > 2) {
state.lastTouch = touch;
state.reachMaxAngle = false;
}
return false;
}
}
}
state.lastTouch = touch;
return true;
}
// 进一步处理是否在下拉刷新并通知js层
function _handlePullingDown(state, ins, onPullingDown) {
var oldOnPullingDown = state.onPullingDown || false;
if (oldOnPullingDown != onPullingDown) {
ins.callMethod('_handleWxsPullingDownStatusChange', onPullingDown);
}
state.onPullingDown = onPullingDown;
}
// 判断js层传过来的值是否为true
function _isTrue(value) {
value = (typeof(value) === 'string' ? JSON.parse(value) : value) || false;
return value == true || value == 'true';
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
mousedown: mousedown,
mousemove: mousemove,
mouseup: mouseup,
mouseleave: mouseleave,
propObserver: propObserver
}