1.当开始一个touchstart事件的时候,获取此刻手指的横坐标startX和纵坐标startY;
当触发touchmove事件时,在获取此时手指的横坐标moveEndX和纵坐标moveEndY;最后,通过这两次获取的坐标差值来判断手指在手机屏幕上的滑动方向。
思路:用touchmove的最后坐标减去touchstart的起始坐标,X的结果如果正数,则说明手指是从左往右划动;X的结果如果负数,则说明手指是从右往左划动;Y的结果如果正数,则说明手指是从上往下划动;Y的结果如果负数,则说明手指是从下往上划动。
2.然而在实际的操作中,手指的上下滑动很难做到直上直下,只要稍微有点斜,只要稍微有点斜,就会被X轴的判断先行接管,而与我们实际的操作意愿相背离。
解决方案:
通过角度判断:以起点做平面坐标系,与终点连线做直线,直线与x正半轴计算角度;我们以45度角为方向分割线,如:只要滑动角度大于等于45度且小于135度,则判断它方向为向上滑。
同时加入时间差,在一定时间内触发滑动
3.使用Math.atan2来计算起点与终点形成的直线角度。
注意:标准坐标系与屏幕坐标系并不相同,在屏幕坐标系中,上半轴为负值,要实现转换,只需要调换Y坐标起点与终于位置即可。
4.位置限制
手指从左右红框开始按照一定时间,一定距离,一定角度滑动,会触发响应事件
中间部分即便会存在以上情况,都不响应
import React, { useState } from 'react';
import { View } from '@tarojs/components';
import Taro from '@tarojs/taro';
const PageTouch = ({ children, handlePreviousPage, handleNextPage, pre_id, next_id }) => {
const [touchStart, setTouchStart] = useState([0, 0]);
const [touchEnd, setTouchEnd] = useState([0, 0]);
const [startTime, setStartTime] = useState(0);
const handleTouchStart = e => {
const sx = e.touches[0].pageX;
const sy = e.touches[0].pageY;
setTouchStart([sx, sy]);
setStartTime(new Date().getTime()); // 获取毫秒数
};
const handleTouchMove = e => {
const sx = e.touches[0].pageX;
const sy = e.touches[0].pageY;
setTouchEnd([sx, sy]);
};
// 返回角度
function GetSlideAngle(dx, dy) {
return (Math.atan2(dy, dx) * 180) / Math.PI;
}
// 根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
function GetSlideDirection(startX, startY, endX, endY) {
const dy = startY - endY;
const dx = endX - startX;
let result = 'static';
let name = '未滑动';
// 如果滑动距离太短
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
return result;
}
const angle = GetSlideAngle(dx, dy);
if (angle >= -20 && angle < 45) {
result = 'right';
name = '向右';
} else if (angle >= 45 && angle < 135) {
result = 'up';
name = '向上';
} else if (angle >= -135 && angle < -45) {
result = 'down';
name = '向下';
} else if ((angle >= 150 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 'left';
name = '向左';
}
return {
AngleStatus: result,
angle,
name,
};
}
const handleTouchEnd = () => {
const strX = touchStart[0];
const endX = touchEnd[0];
const strY = touchStart[1];
const endY = touchEnd[1];
const { AngleStatus, angle, name } = GetSlideDirection(strX, strY, endX, endY);
console.log('滑动角度:', `${(angle, name)}`);
const X = strX - endX;
const Y = endY - strY;
const absY = Math.abs(endY - strY);
const absX = Math.abs(strX - endX);
const minTime = 110; // 最小时间,单位:毫秒,低于这个值不响应滑动处理
const scrollHeightRight = 30; // Y轴滑动距离
const scrollHeightLeft = 30; // Y轴滑动距离
const touchTime = new Date().getTime() - startTime; // 计算滑动时间
const alowchngePage = touchTime >= minTime && absX > 50;
const log = `strX:${strX},endX:${endX},偏移x:${absX}`;
if (
absX > 90 &&
X < 0 &&
AngleStatus === 'right' &&
touchTime >= minTime &&
absY < scrollHeightRight &&
strX < 100
) {
console.log('向右');
Taro.showToast({
title: `右滑,${log}`,
icon: 'none',
});
// if (!pre_id) {
// return Taro.showToast({
// title: '已经是第一页',
// icon: 'none',
// });
// }
// handlePreviousPage();
} else if (
absX > 30 &&
X > 0 &&
AngleStatus === 'left' &&
touchTime >= minTime &&
absY < scrollHeightLeft &&
strX > 300
) {
console.log('向左');
Taro.showToast({
title: `左滑,${log}`,
icon: 'none',
});
// if (!next_id) {
// return Taro.showToast({
// title: '已经是最后一页',
// icon: 'none',
// });
// }
// handleNextPage();
} else if (absY > 50 && Y > 0 && AngleStatus === 'down') {
console.log('向下');
Taro.showToast({
title: `向下,${log}`,
icon: 'none',
});
} else if (absY > 50 && Y < 0 && AngleStatus === 'up') {
console.log('向上');
Taro.showToast({
title: `向上,${log}`,
icon: 'none',
});
} else {
console.log('没滑动');
Taro.showToast({
title: `没滑动,${log}`,
icon: 'none',
});
}
setTouchStart([0, 0]);
setTouchEnd([0, 0]);
};
return (
<View
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
{children}
</View>
);
};
export default PageTouch;
增加动画
import React, { useState } from 'react';
import { View } from '@tarojs/components';
import Taro from '@tarojs/taro';
// 返回角度
function GetSlideAngle(dx, dy) {
return (Math.atan2(dy, dx) * 180) / Math.PI;
}
// 根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
function GetSlideDirection(startX, startY, endX, endY) {
const dy = startY - endY;
const dx = endX - startX;
let result = 'static';
let name = '未滑动';
// 如果滑动距离太短
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
return result;
}
const angle = GetSlideAngle(dx, dy);
if (angle >= -20 && angle < 45) {
result = 'right';
name = '向右';
} else if (angle >= 45 && angle < 135) {
result = 'up';
name = '向上';
} else if (angle >= -135 && angle < -45) {
result = 'down';
name = '向下';
} else if ((angle >= 150 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 'left';
name = '向左';
}
return {
AngleStatus: result,
angle,
name,
};
}
const PageTouch = ({ children, handlePreviousPage, handleNextPage, pre_id, next_id }) => {
const [touchStart, setTouchStart] = useState([0, 0]);
const [touchEnd, setTouchEnd] = useState([0, 0]);
const [startTime, setStartTime] = useState(0);
const [aniRight, setAniRight] = useState('');
const [aniLeft, setAniLeft] = useState('');
const [aniT, setAniT] = useState(false);
// 向右滑动操作
const moveAniRight = () => {
setAniT(false);
const animation = Taro.createAnimation({
duration: 1000,
timingFunction: 'ease',
delay: 100,
});
animation
.opacity(0.2)
.translate(500, 0)
.step();
setAniRight(animation.export());
setTimeout(() => {
setAniRight('');
}, 800);
};
// 向左滑动操作
const moveAniLeft = () => {
setAniT(true);
const animation = Taro.createAnimation({
duration: 1000,
timingFunction: 'ease',
delay: 100,
});
animation
.opacity(0.2)
.translate(-500, 0)
.step();
setAniLeft(animation.export());
setTimeout(() => {
setAniLeft('');
}, 800);
};
const handleChangePage = (strX, strY, endX, endY, ifMove) => {
const { AngleStatus } = GetSlideDirection(strX, strY, endX, endY);
const X = strX - endX;
const Y = endY - strY;
const absY = Math.abs(endY - strY);
const absX = Math.abs(strX - endX);
const minTime = 110; // 最小时间,单位:毫秒,低于这个值不响应滑动处理
const scrollHeightRight = 30; // Y轴滑动距离
const scrollHeightLeft = 30; // Y轴滑动距离
const touchTime = new Date().getTime() - startTime; // 计算滑动时间
// const alowchngePage = touchTime >= minTime && absX > 50;
// const log = `角度:${angle},x偏移:${absX},时间:${touchTime}`;
if (
absX >= 50 &&
X < 0 &&
AngleStatus === 'right' &&
touchTime >= minTime &&
absY < scrollHeightRight &&
strX < 100
) {
// Taro.showToast({
// title: `右滑,${log}`,
// icon: 'none',
// });
if (!pre_id) {
return Taro.showToast({
title: '已经是第一页',
icon: 'none',
});
}
if (ifMove) {
moveAniRight();
} else {
handlePreviousPage();
}
} else if (
absX > 40 &&
X > 0 &&
AngleStatus === 'left' &&
touchTime >= minTime &&
absY < scrollHeightLeft &&
strX > 300
) {
// Taro.showToast({
// title: `左滑,${log}`,
// icon: 'none',
// });
if (!next_id) {
return Taro.showToast({
title: '已经是最后一页',
icon: 'none',
});
}
if (ifMove) {
moveAniLeft();
} else {
handleNextPage();
}
} else if (absY > 50 && Y > 0 && AngleStatus === 'down') {
// Taro.showToast({
// title: `向下,${log}`,
// icon: 'none',
// });
} else if (absY > 50 && Y < 0 && AngleStatus === 'up') {
// Taro.showToast({
// title: `向上,${log}`,
// icon: 'none',
// });
} else {
// Taro.showToast({
// title: `没滑动,${log}`,
// icon: 'none',
// });
}
};
const handleTouchStart = e => {
const sx = e.touches[0].pageX;
const sy = e.touches[0].pageY;
setTouchStart([sx, sy]);
setStartTime(new Date().getTime()); // 获取毫秒数
};
const handleTouchMove = e => {
const endX = e.touches[0].pageX;
const endY = e.touches[0].pageY;
setTouchEnd([endX, endY]);
const strX = touchStart[0];
const strY = touchStart[1];
handleChangePage(strX, strY, endX, endY, true);
};
const handleTouchEnd = () => {
const strX = touchStart[0];
const endX = touchEnd[0];
const strY = touchStart[1];
const endY = touchEnd[1];
handleChangePage(strX, strY, endX, endY, false);
setTouchStart([0, 0]);
setTouchEnd([0, 0]);
};
return (
<View
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
animation={aniT ? aniLeft : aniRight}
>
{children}
</View>
);
};
export default PageTouch;