@cataract6545/tmui
Version:
tm-vuetify是一个新势力由主题驱动的UI组件库,相比其它优势大,组件全,设计趋势紧跟未来。具有主题生成,主题实时切换,暗黑实时切换,lottie动画,图表等新颖功能,tmui TMUI
305 lines (269 loc) • 9.88 kB
text/typescript
import { ref, reactive } from 'vue';
interface positionType {
x: number | null,
y: number | null
}
/**
* 触摸事件
* @example
```
import { useTouchFinger } from '@/tmui/tool/useFun/useTouchFinger'
const {touchstart,touchmove,touchend,touchcancel,addEventListener,direction,deltaXY,preTapPosition,angle,scale } = useTouchFinger();
<image
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
@touchcancel="touchcancel"
src="https://pic.rmb.bdstatic.com/bjh/beautify/aee57799c6885386bb748e07fe43f78f.jpeg"
:style="{transform:`scale(${scale}) rotate(${angle}deg)`,width:'100px',height:'100px'}"></image>
addEventListener('rotate',(evt)=>{
console.log(direction.value)
})
```
*/
export const useTouchFinger = () => {
const x1 = ref<number | null>(null)
const x2 = ref<number | null>(null)
const y1 = ref<number | null>(null)
const y2 = ref<number | null>(null)
//当前点击的位置
const preTapPosition = reactive<positionType>({ x: null, y: null });
const preV = reactive<positionType>({ x: null, y: null });
//计算的距离是相对上一次移动的距离。
const deltaXY = reactive<positionType>({ x: null, y: null });
//计算的位置是相对上次触摸开始的位置,位置以当前元素在相对父级左上角开始的位置。
const positionXY = reactive<positionType>({ x: 0, y: 0 });
const pinchStartLen = ref(0);
const scale = ref(1);
const angle = ref(0);
const direction = ref<"" | "Left" | "Right" | "Up" | "Down">("");
const testScale = ref(0)
let delta = 0;
let last = 0;
let now = 0;
let isDoubleTap = false;
let tapTimeout: any = NaN;
let singleTapTimeout: any = NaN;
let longTapTimeout: any = NaN;
let swipeTimeout: any = NaN;
const zoomFactor = 0.55; // 缩放速率
const zoomFactorAb = 0.03; // 旋转速率
// 点按
let tap = (evt: TouchEvent) => { }
//双指捏合
let pinch = (evt: TouchEvent) => { }
//双指撮合旋转
let rotate = (evt: TouchEvent) => { }
let touchStart = (evt: TouchEvent) => { }
let touchMove = (evt: TouchEvent) => { }
let touchEnd = (evt: TouchEvent) => { }
let touchCancel = (evt: TouchEvent) => { }
//手指按住移动
let pressMove = (evt: TouchEvent) => { }
//双击
let doubleTap = (evt: TouchEvent) => { }
//长按
let longTap = (evt: TouchEvent) => { }
//手指滑动
let swipe = (evt: TouchEvent) => { }
//单击
let singleTap = (evt: TouchEvent) => { }
//一个手指以上触发
let multipointStart = (evt: TouchEvent) => { }
//一个手指以上触发结束
let multipointEnd = (evt: TouchEvent) => { }
function start(evt: TouchEvent) {
if (!evt.touches) return;
touchStart(evt)
now = Date.now();
x1.value = evt.touches[0].pageX;
y1.value = evt.touches[0].pageY;
delta = now - (last || now);
if(x1.value===null) x1.value = 0
if(y1.value===null) y1.value = 0
if (preTapPosition.x !== null && preTapPosition.y !== null) {
isDoubleTap = (delta > 0 && delta <= 250 && Math.abs(preTapPosition.x - x1.value) < 30 && Math.abs(preTapPosition.y - y1.value) < 30);
}
preTapPosition.x = x1.value;
preTapPosition.y = y1.value;
last = now;
let len = evt.touches.length;
if (len > 1) {
cancelLongTap();
cancelSingleTap();
let otx = evt.touches[1].pageX;
let oty = evt.touches[1].pageY;
let v = { x: otx - x1.value, y: oty - y1.value };
preV.x = v.x;
preV.y = v.y;
pinchStartLen.value = getLen(preV);
multipointStart(evt)
}
longTapTimeout = setTimeout(function () {
longTap(evt);
}, 750);
}
function move(evt: TouchEvent) {
if (!evt.touches) return;
let len = evt.touches.length,
currentX = evt.touches[0].pageX,
currentY = evt.touches[0].pageY;
isDoubleTap = false;
if (len > 1) {
let otx = evt.touches[1].pageX;
let oty = evt.touches[1].pageY;
let v = { x: otx - currentX, y: oty - currentY };
if (preV.x !== null) {
if (pinchStartLen.value > 0) {
let temsc = (getLen(v) / pinchStartLen.value);
// 计算缩放比例
const deltaScale = (temsc - 1) * zoomFactor + scale.value;
scale.value = Math.max(deltaScale,0.1)
pinch(evt);
}
let testjd = getRotateAngle(v, preV);
angle.value = Math.floor((testjd - 1) * zoomFactorAb) + angle.value;
pinchStartLen.value = getLen(v);
rotate(evt);
}
preV.x = v.x;
preV.y = v.y;
} else {
if (x2.value !== null && y2.value !== null) {
deltaXY.x = currentX - x2.value;
deltaXY.y = currentY - y2.value;
} else {
deltaXY.x = 0;
deltaXY.y = 0;
}
pressMove(evt);
}
touchMove(evt);
cancelLongTap();
x2.value = currentX;
y2.value = currentY;
if (len > 1) {
evt?.preventDefault();
}
}
function end(evt: TouchEvent) {
if (!evt.changedTouches) return;
cancelLongTap();
if (evt.touches.length < 2) {
multipointEnd(evt);
}
touchEnd(evt);
if(x1.value===null) x1.value = 0
if(x2.value===null) x2.value = 0
if(y1.value===null) y1.value = 0
if(y2.value===null) y2.value = 0
//swipe
if ((x2.value && Math.abs(x1.value - x2.value) > 30) ||
(y2.value && Math.abs(y1.value - y2.value) > 30)) {
direction.value = swipeDirection(x1.value, x2.value, y1.value, y2.value);
swipeTimeout = setTimeout(function () {
swipe(evt);
}, 0)
} else {
tapTimeout = setTimeout(function () {
tap(evt);
if (isDoubleTap) {
doubleTap(evt);
scale.value = 1;
angle.value = 0
clearTimeout(singleTapTimeout);
isDoubleTap = false;
}
}, 0)
if (!isDoubleTap) {
singleTapTimeout = setTimeout(function () {
singleTap(evt);
}, 250);
}
}
preV.x = 0;
preV.y = 0;
// scale.value = 1;
pinchStartLen.value = 0;
x1.value = x2.value = y1.value = y2.value = null;
}
function bind(
type: "tap" | "pinch" | "rotate" | "touchStart" | "touchMove" | "touchEnd" | "touchCancel"
| "pressMove" | "doubleTap" | "longTap" | "swipe" | "singleTap" | "multipointStart" | "multipointEnd",
call: (evt: TouchEvent) => void
) {
if (type == 'tap') tap = call;
if (type == 'pinch') pinch = call;
if (type == 'rotate') rotate = call;
if (type == 'touchStart') touchStart = call;
if (type == 'touchMove') touchMove = call;
if (type == 'touchEnd') touchEnd = call;
if (type == 'touchCancel') touchCancel = call;
if (type == 'pressMove') pressMove = call;
if (type == 'doubleTap') doubleTap = call;
if (type == 'longTap') longTap = call;
if (type == 'swipe') swipe = call;
if (type == 'singleTap') singleTap = call;
if (type == 'multipointStart') multipointStart = call;
if (type == 'multipointEnd') multipointEnd = call;
}
function swipeDirection(x1: number, x2: number, y1: number, y2: number) {
return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}
function getLen(v: positionType) {
if(v.x===null||v.y===null) return 0
return Math.hypot(v.x,v.y)
// return Math.sqrt(v.x * v.x + v.y * v.y);
}
function dot(v1: positionType, v2: positionType) {
if(v1.x===null||v1.y===null||v2.x===null||v2.y===null) return 0
return v1.x * v2.x + v1.y * v2.y;
}
function getAngle(v1: positionType, v2: positionType) {
let mr = getLen(v1) * getLen(v2);
if (mr === 0) return 0;
let r = dot(v1, v2) / mr;
if (r > 1) r = 1;
let jd = Math.acos(r);
jd = jd * (180 / Math.PI);
return (jd + 360) % 360;;
}
function cross(v1: positionType, v2: positionType) {
if(v1.x===null||v1.y===null||v2.x===null||v2.y===null) return 0
return v1.x * v2.y - v2.x * v1.y;
}
function getRotateAngle(v1: positionType, v2: positionType) {
let angle = getAngle(v1, v2);
if (cross(v1, v2) > 0) {
angle *= -1;
}
return angle * 180 / Math.PI;
}
function cancel(evt: TouchEvent) {
clearTimeout(singleTapTimeout);
clearTimeout(tapTimeout);
clearTimeout(longTapTimeout);
clearTimeout(swipeTimeout);
touchCancel(evt);
}
function cancelLongTap() {
clearTimeout(longTapTimeout);
}
function cancelSingleTap() {
clearTimeout(singleTapTimeout);
}
return {
touchstart: start,
touchmove: move,
touchend: end,
touchcancel: cancel,
addEventListener: bind,
deltaXY,
pinchStartLen,
scale,
angle,
direction,
preTapPosition,
testScale
}
}