Files
2025-08-15 09:56:40 +08:00

318 lines
11 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.

// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
// https://uniapp.dcloud.io/frame?id=wxs
// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html
// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
var me = {}
// ------ 自定义下拉刷新动画 start ------
/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset)*/
me.onMoving = function (ins, rate, downHight){
var isLockCallback = (rate >= 2); //大于2倍offset,自定义下拉刷新,进入淘宝二楼
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
'transform': 'translateY(' + downHight + 'px)',
'transition': ''
})
// 显示淘宝二楼,则不再更新进度条
if(isLockCallback) return;
var leftAct = ins.selectComponent('.wxs-left-arc');
if(!leftAct) return;
var rightAct = ins.selectComponent('.wxs-right-arc');
if(!rightAct) return;
// 左半圆
var rotateLeft = ''
if (rate<1) {
var progressLeft = 360 * rate
if (progressLeft<180) {
rotateLeft = 'rotate(45deg)';
} else{
rotateLeft = 'rotate(' + (progressLeft-180+45) + 'deg)';
}
}else{
rotateLeft = 'rotate(225deg)';
}
leftAct.setStyle({
transform: rotateLeft
})
//
var rotateRight = ""
var progressRight = 360 * rate;
if (progressRight<180) {
rotateRight = 'rotate(' + (progressRight+45) + 'deg)';
} else{
rotateRight = 'rotate(225deg)';
}
rightAct.setStyle({
transform: rotateRight
})
})
// wxsCallLock
if(me.isLockCallback != isLockCallback){
me.isLockCallback=isLockCallback //
ins.callMethod('wxsCallLock', {rate:rate, isLockCallback:isLockCallback})
}
}
/* */
me.showLoading = function (ins){
me.downHight = me.optDown.offset
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': 'auto',
'transform': 'translateY(' + me.downHight + 'px)',
'transition': 'transform 300ms'
})
})
}
/* */
me.endDownScroll = function (ins){
me.downHight = 0;
me.isDownScrolling = false;
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': 'auto',
'transform': 'translateY(0)', // ,scroll-view (350msclearTransform)
'transition': 'transform 300ms'
})
})
}
/* , transformtransition, , : h5list-msg */
me.clearTransform = function (ins){
ins.requestAnimationFrame(function () {
ins.selectComponent('.mescroll-wxs-content').setStyle({
'will-change': '',
'transform': '',
'transition': ''
})
})
}
/* */
me.showErlou = function (ins){
ins.requestAnimationFrame(function () {
var downHight = 2 * me.getBodyHeight(); //
ins.selectComponent('.mescroll-wxs-content').setStyle({
transform: 'translateY(' + downHight + 'px)',
transition: 'transform 300ms'
})
})
}
// ------ end ------
/**
* ()
*/
function propObserver(wxsProp) {
me.optDown = wxsProp.optDown
me.scrollTop = wxsProp.scrollTop
me.bodyHeight = wxsProp.bodyHeight
me.isDownScrolling = wxsProp.isDownScrolling
me.isUpScrolling = wxsProp.isUpScrolling
me.isUpBoth = wxsProp.isUpBoth
me.isScrollBody = wxsProp.isScrollBody
me.startTop = wxsProp.scrollTop // touchstartstartTop, scroll-view
}
/**
* (wxs)
*/
function callObserver(callProp, oldValue, ins) {
if (me.disabled()) return;
if(callProp.callType){
// App Servicestyle,Webviewstyle
if(callProp.callType === 'showLoading'){
me.showLoading(ins)
}else if(callProp.callType === 'endDownScroll'){
me.endDownScroll(ins)
}else if(callProp.callType === 'clearTransform'){
me.clearTransform(ins)
}else if(callProp.callType === 'showErlou'){
me.showErlou(ins)
}
}
}
/**
* touch
*/
function touchstartEvent(e, ins) {
me.downHight = 0; //
me.startPoint = me.getPoint(e); //
me.startTop = me.getScrollTop(); //
me.startAngle = 0; //
me.lastPoint = me.startPoint; // move
me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // (touchstartbody0)
me.inTouchend = false; // touchend
me.callMethod(ins, {type: 'setWxsProp'}) // wxsProp (,touchmove,propObserver; h5app)
}
function touchmoveEvent(e, ins) {
var isPrevent = true // falsestopPropagationpreventDefault (, h5app)
if (me.disabled()) return isPrevent;
var scrollTop = me.getScrollTop(); //
var curPoint = me.getPoint(e); //
var moveY = curPoint.y - me.startPoint.y; // ,,0,0
// &&
// mescroll-body,
// scroll-viewtouchmove,///,touchmove
// scroll-view,scrollTop0; iOSAPPscrollTop,startTop
if (moveY > 0 && (
(me.isScrollBody && scrollTop <= 0)
||
(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
)) {
// 可下拉的条件
if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
me.isUpBoth))) {
// 下拉的角度是否在配置的范围内
if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
if (me.startAngle < me.optDown.minAngle) return isPrevent; // ,
// ,,Webviewtouchend
if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
me.inTouchend = true; // 标记执行touchend
touchendEvent(e, ins); // 提前触发touchend
return isPrevent;
}
isPrevent = false // 小程序是return false
var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
// 下拉距离 < 指定距离
if (me.downHight < me.optDown.offset) {
if (me.movetype !== 1) {
me.movetype = 1; // ,
// me.optDown.inOffset && me.optDown.inOffset(me); // ,
me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
me.isMoveDown = true; // ,touchend
}
me.downHight += diff * me.optDown.inOffsetRate; // ,
// <=
} else {
if (me.movetype !== 2) {
me.movetype = 2; // ,
// me.optDown.outOffset && me.optDown.outOffset(me); // ,
me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
me.isMoveDown = true; // ,touchend
}
if (diff > 0) { // 向下拉
me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
} else { // 向上收
me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
}
}
me.downHight = Math.round(me.downHight) // 取整
var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
me.onMoving(ins, rate, me.downHight)
}
}
me.lastPoint = curPoint; // 记录本次移动的点
return isPrevent // false表示不往上冒泡相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
}
function touchendEvent(e, ins) {
// 如果下拉区域高度已改变,则需重置回来
if (me.isMoveDown) {
if (me.downHight >= me.optDown.offset) {
// 符合触发刷新的条件
me.downHight = me.optDown.offset; // 更新下拉区域高度
// me.triggerDownScroll();
me.callMethod(ins, {type: 'triggerDownScroll'})
} else {
// 不符合的话 则重置
me.downHight = 0;
// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
me.callMethod(ins, {type: 'endDownScroll'})
}
me.movetype = 0;
me.isMoveDown = false;
} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // ,,0,0
//
if (isScrollUp) {
//
var angle = me.getAngle(me.getPoint(e), me.startPoint); // , [0,90]
if (angle > 80) {
// 检查并触发上拉
// me.triggerUpScroll(true);
me.callMethod(ins, {type: 'triggerUpScroll'})
}
}
}
me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
}
/*是否禁用*/
me.disabled = function(){
return !me.optDown || !me.optDown.use || me.optDown.native
}
/* 根据点击滑动事件获取第一个手指的坐标 */
me.getPoint = function(e) {
if (!e) {
return {x: 0,y: 0}
}
if (e.touches && e.touches[0]) {
return {x: e.touches[0].pageX,y: e.touches[0].pageY}
} else if (e.changedTouches && e.changedTouches[0]) {
return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
} else {
return {x: e.clientX,y: e.clientY}
}
}
/* 计算两点之间的角度: 区间 [0,90]*/
me.getAngle = function (p1, p2) {
var x = Math.abs(p1.x - p2.x);
var y = Math.abs(p1.y - p2.y);
var z = Math.sqrt(x * x + y * y);
var angle = 0;
if (z !== 0) {
angle = Math.asin(y / z) / Math.PI * 180;
}
return angle
}
/* 获取滚动条的位置 */
me.getScrollTop = function() {
return me.scrollTop || 0
}
/* 获取body的高度 */
me.getBodyHeight = function() {
return me.bodyHeight || 0;
}
/* 调用逻辑层的方法 */
me.callMethod = function(ins, param) {
if(ins) ins.callMethod('wxsCall', param)
}
/* 导出模块 */
module.exports = {
propObserver: propObserver,
callObserver: callObserver,
touchstartEvent: touchstartEvent,
touchmoveEvent: touchmoveEvent,
touchendEvent: touchendEvent
}