Files
hospital-rescue-app-v2/uni_modules/tmx-ui/components/x-finger/x-finger.uvue
T
2026-09-24 16:25:22 +08:00

899 lines
23 KiB
Plaintext

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
import { getUid } from '../../core/util/xCoreUtil.uts';
type CHECKPOINT_XY = { x : number | null, y : number | null }
type DRect = {
left:number,
right:number,
top:number,
bottom:number,
width:number,
height:number,
}
/**
* @name 手势库 xFinger
* @description 多方向的手势库,包括旋转,捏合,轻扫,双击等手势
* @page /pages/index/finger
* @category 其它组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({name:"xFinger"})
type Props = {
/**
* 滑动多长距离,识别并触发滑动的方向
*/
swiperDiff: number
/**
* 定义双击的时间间隔触发时机
*/
dbClickDiff: number
/**
* 定义单击click的时间间隔触发时机
*/
clickDiff: number
/**
* 定义长按的时间间隔触发时机
*/
longDiff: number
/**
* 是否禁用
*/
disabled: boolean
/**
* 移动事件的节流间隔(毫秒),0表示不节流
*/
throttleDelay: number
/**
* 是否启用防抖,防止快速连续触发
*/
debounce: boolean
/**
* 最小移动距离,小于此距离不触发移动事件
*/
minMoveDistance: number
/**
* 连续性坐标初始位置x
*/
accOffsetX:number,
/**
* 连续性坐标初始位置y
*/
accOffsetY:number,
/**
* 是否启用无障碍访问支持
*/
accessibility: boolean
/**
* 无障碍标签
*/
ariaLabel: string
}
defineSlots<{
/**
* 默认插槽default
*/
default(props:{
/**
* 当前x坐标
*/
x:number,
/**
* 当前y坐标
*/
y:number,
/**
* 累积x坐标(连续定位)
*/
accX:number,
/**
* 累积y坐标(连续定位)
*/
accY:number,
/**
* 当前事件类型click,start,end,dbclick,longPress,swiper,pinch,rotate
*/
type:string
}):any
}>()
const props = withDefaults(defineProps<Props>(), {
swiperDiff: 50,
dbClickDiff: 300,
clickDiff: 50,
longDiff: 800,
disabled: false,
throttleDelay: 16, // 约60fps
debounce: false,
minMoveDistance: 1,
accessibility: true,
accOffsetX:0,
accOffsetY:0,
ariaLabel: '手势识别区域'
})
// Emits
const emit = defineEmits<{
/**
* 触摸开始
* @param evt - {x,y,type,width,height}
*/
start: [evt: {x: number, y: number, type: string, width: number, height: number}]
/**
* 触摸移动时触发
* @param evt - {x,y,type,width,height}
*/
move: [evt: {x: number, y: number, type: string, width: number, height: number}]
/**
* 触摸结束
* @param evt - {x,y,type,width,height}
*/
end: [evt: {x: number, y: number, type: string, width: number, height: number}]
/**
* 触摸中断
* @param evt - {x,y,type,width,height}
*/
cancel: [evt: {x: number, y: number, type: string, width: number, height: number}]
/**
* doubleClick
* @param evt - {x,y,type,width,height}
*/
doubleClick: [evt: {x: number, y: number, type: string, width: number, height: number}]
/**
* 长按事件
* @param evt - {x,y,type,width,height}
*/
longPress: [evt: {x: number, y: number, type: string, width: number, height: number}]
/**
* 滑动时触发
* @param evt - {x,y,type,width,height,direction:方向UP|DOWN|LEFT|RIGHT}
*/
swiper: [evt: {x: number, y: number, diffX: number, diffY: number, direction: string, type: string, width: number, height: number}]
/**
* 单击
* @param evt - {x,y,type,width,height}
*/
click: [evt: {x: number, y: number, type: string, width: number, height: number}]
/**
* 缩放事件
* @description len为两点间的距离,scale为当前的缩放比例(最小为0.1)
* @param evt - {x,y,x1,y1,len,scale,type,width,height}
*/
pinch: [evt: {x: number, y: number, x1: number, y2: number, type: string, width: number, height: number, len: number, scale: number}]
/**
* 旋转事件
* @description len为两点间的距离,angle为当前当前旋转的角度
* @param evt - {x,y,x1,y1,len,angle,type,width,height}
*/
rotate: [evt: {x: number, y: number, x1: number, y2: number, type: string, width: number, height: number, len: number, angle: number}]
}>()
// Reactive data
const isMouseDown = ref(false)
const wheelScale = ref(1)
const wheelDelta = ref(0.1) // 滚轮缩放系数
const dubleTime = ref(0)
const tid = ref(56)
const _x = ref(0)
const _y = ref(0)
const _start_x = ref(0)
const _start_y = ref(0)
const eventName = ref('')
const mX = ref(0)
const mY = ref(0)
const swipeDirection = ref("")
// 累积定位坐标,保持连续性
const accumulatedX = ref(props.accOffsetX)
const accumulatedY = ref(props.accOffsetY)
// 本次手势开始时的累积坐标
const startAccumulatedX = ref(0)
const startAccumulatedY = ref(0)
const xFingerRef = ref<UniElement | null>(null)
const left = ref(0)
const top = ref(0)
const id = ref("xFinGer" + getUid())
const zoomFactor = ref(0.55) // 缩放速率
const zoomFactorAb = ref(0.03) // 旋转速率
const pinchStartLen = ref(0)
const scale = ref(0)
const angle = ref(0)
const pinth_x = ref(0)
const pinth_y = ref(0)
const preV = ref<CHECKPOINT_XY>({ x: null, y: null })
const parentRect = ref<DRect>({
left:0,
right:0,
top:0,
bottom:0,
width:0,
height:0,
})
// 性能优化相关
const lastMoveTime = ref(0)
const throttleTimer = ref<number | null>(null)
const debounceTimer = ref<number | null>(null)
// Computed
const _disabled = computed(() => props.disabled)
// Get current instance
const instance = getCurrentInstance()?.proxy;
// 工具函数
const throttle = (func: Function, delay: number) => {
return (...args: any[]) => {
const now = Date.now()
if (now - lastMoveTime.value >= delay) {
lastMoveTime.value = now
func.apply(null, args)
}
}
}
const debounce = (func: Function, delay: number) => {
return (...args: any[]) => {
if (debounceTimer.value) {
clearTimeout(debounceTimer.value)
}
debounceTimer.value = setTimeout(() => {
func.apply(null, args)
}, delay)
}
}
const calculateDistance = (x1: number, y1: number, x2: number, y2: number): number => {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
}
const isGestureValid = (deltaX: number, deltaY: number): boolean => {
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
return distance >= props.minMoveDistance
}
// #ifdef WEB
// 处理鼠标滚轮事件,模拟缩放功能
const handleWheel = (evt: WheelEvent) => {
if (_disabled.value) return
const delta = evt.deltaY > 0 ? -wheelDelta.value : wheelDelta.value;
wheelScale.value = Math.max(0.1, wheelScale.value + delta);
emit("pinch", {
x: evt.clientX,
y: evt.clientY,
type: 'pinch',
width: parentRect.value.width,
height: parentRect.value.height,
len: 0,
scale: wheelScale.value
});
}
// 鼠标事件处理函数
const mouseStart = (evt: MouseEvent) => {
if (_disabled.value) return
isMouseDown.value = true;
let rectBox = parentRect.value;
_x.value = evt.clientX - rectBox.left;
_y.value = evt.clientY - rectBox.top;
// 重置移动距离,开始新的手势
mX.value = 0
mY.value = 0
// 记录本次手势开始时的累积坐标
startAccumulatedX.value = accumulatedX.value
startAccumulatedY.value = accumulatedY.value
_start_x.value = evt.clientX - rectBox.left;
_start_y.value = evt.clientY - rectBox.top;
swipeDirection.value = "";
emit("start", {
x: _start_x.value,
y: _start_y.value,
type: 'start',
width: rectBox.width,
height: rectBox.height
});
eventName.value = 'start';
// 处理双击
let difftime = new Date().getTime() - dubleTime.value;
if (difftime > 0 && difftime <= props.dbClickDiff) {
emit("doubleClick", {
x: _start_x.value,
y: _start_y.value,
type: 'doubleClick',
width: rectBox.width,
height: rectBox.height
});
eventName.value = 'doubleClick';
}
dubleTime.value = new Date().getTime();
// 处理长按
clearTimeout(tid.value);
tid.value = setTimeout(() => {
if (isMouseDown.value) {
emit("longPress", {
x: _start_x.value,
y: _start_y.value,
type: 'longPress',
width: rectBox.width,
height: rectBox.height
});
eventName.value = 'longPress';
}
}, props.longDiff);
}
const mouseMove = (evt: MouseEvent) => {
if (_disabled.value || !isMouseDown.value) return
let rectBox = parentRect.value;
let x = evt.clientX - rectBox.left;
let y = evt.clientY - rectBox.top;
let deltaX = Math.abs(x);
let deltaY = Math.abs(y);
mX.value = Math.max(0, Math.min(rectBox.width, x)) - _start_x.value
mY.value = Math.max(0, Math.min(rectBox.height, y)) - _start_y.value
// 在移动过程中实时更新累积坐标(用于插槽输出)
accumulatedX.value = startAccumulatedX.value + mX.value
accumulatedY.value = startAccumulatedY.value + mY.value
if (deltaX > deltaY && deltaX > props.swiperDiff) {
swipeDirection.value = (_x.value > x) ? "left" : "right";
} else if (deltaY > deltaX && deltaY > props.swiperDiff) {
swipeDirection.value = (_y.value < y) ? "down" : "up";
}
if (swipeDirection.value != "") {
emit("swiper", {
x: mX.value,
y: mY.value,
diffX: deltaX,
diffY: deltaY,
direction: swipeDirection.value,
type: 'swiper',
width: rectBox.width,
height: rectBox.height
});
eventName.value = 'swiper';
}
emit("move", {
x: mX.value,
y: mY.value,
type: 'move',
width: rectBox.width,
height: rectBox.height
});
eventName.value = 'move';
clearTimeout(tid.value);
}
const mouseEnd = (evt: MouseEvent) => {
if (_disabled.value) return
let rectBox = parentRect.value;
let x = evt.clientX - rectBox.left;
let y = evt.clientY - rectBox.top;
let deltaX = Math.abs(x);
let deltaY = Math.abs(y);
mX.value = Math.max(0, Math.min(rectBox.width, x)) - _start_x.value
mY.value = Math.max(0, Math.min(rectBox.height, y)) - _start_y.value
// 更新累积坐标(手势结束时累加本次移动的偏移量)
accumulatedX.value = startAccumulatedX.value + mX.value
accumulatedY.value = startAccumulatedY.value + mY.value
emit("end", {
x: mX.value,
y: mY.value,
type: 'end',
width: rectBox.width,
height: rectBox.height
});
eventName.value = 'end';
if (new Date().getTime() - dubleTime.value > props.clickDiff) {
emit("click", {
x: mX.value,
y: mY.value,
type: 'click',
width: rectBox.width,
height: rectBox.height
});
eventName.value = 'click';
}
isMouseDown.value = false;
// 重置所有状态,为下次触摸做准备
swipeDirection.value = ""
scale.value = 0
angle.value = 0
}
const mouseCancel = (evt: MouseEvent) => {
if (_disabled.value) return
let rectBox = parentRect.value;
let x = evt.clientX - rectBox.left;
let y = evt.clientY - rectBox.top;
let deltaX = Math.abs(x);
let deltaY = Math.abs(y);
// mX.value = Math.max(0, Math.min(rectBox.width, x)) - _start_x.value
// mY.value = Math.max(0, Math.min(rectBox.height, y)) - _start_y.value
// emit("cancel", {
// x: mX.value,
// y: mY.value,
// type: 'cancel',
// width: rectBox.width,
// height: rectBox.height
// });
eventName.value = 'cancel';
isMouseDown.value = false;
// 重置所有状态,为下次触摸做准备
swipeDirection.value = ""
scale.value = 0
angle.value = 0
}
// #endif
const getLen = (v : CHECKPOINT_XY) : number => {
if (v.x == null || v.y == null) return 0
return Math.hypot(v.x!, v.y!)
}
const dot = (v1 : CHECKPOINT_XY, v2 : CHECKPOINT_XY) : number => {
if (v1.x == null || v1.y == null || v2.x == null || v2.y == null) return 0
return v1.x! * v2.x! + v1.y! * v2.y!;
}
const getRectBox = (call:(rect:DRect)=>void) => {
try {
if (xFingerRef.value == null) {
console.warn('x-finger: Element not found')
return
}
let rectBox = {
left:0,
right:0,
top:0,
bottom:0,
width:0,
height:0,
} as DRect
xFingerRef.value?.getBoundingClientRectAsync()?.then((res:DOMRect)=>{
rectBox = {
left:res.left,
right:res.right,
top:res.top,
bottom:res.bottom,
width:res.width,
height:res.height,
} as DRect
call(rectBox)
}).catch(err => {
console.error('x-finger: getBoundingClientRectAsync error', err)
})
} catch (error) {
console.error('x-finger: getRectBox error', error)
}
}
const getAngle = (v1 : CHECKPOINT_XY, v2 : CHECKPOINT_XY) : number => {
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;;
}
const cross = (v1 : CHECKPOINT_XY, v2 : CHECKPOINT_XY) : number => {
if (v1.x == null || v1.y == null || v2.x == null || v2.y == null) return 0
return v1.x! * v2.y! - v2.x! * v1.y!;
}
const getRotateAngle = (v1 : CHECKPOINT_XY, v2 : CHECKPOINT_XY) : number => {
let angle = getAngle(v1, v2);
if (cross(v1, v2) > 0) {
angle *= -1;
}
return angle * 180 / Math.PI;
}
const mStart = (evt : UniTouchEvent) => {
if (_disabled.value) return
let event = evt.changedTouches[0]
clearTimeout(tid.value)
let rectBox = parentRect.value
_x.value = event.clientX - rectBox.left;
_y.value = event.clientY - rectBox.top;
// 重置移动距离,开始新的手势
mX.value = 0
mY.value = 0
// 记录本次手势开始时的累积坐标
startAccumulatedX.value = accumulatedX.value
startAccumulatedY.value = accumulatedY.value
_start_x.value = event.clientX - rectBox.left
_start_y.value = event.clientY - rectBox.top
swipeDirection.value = ""
/**
* 触摸开始
* @param evt {object} {x,y,type,width,height}
*/
emit("start", { x: _start_x.value, y: _start_y.value, type: 'start', width: rectBox.width, height: rectBox.height })
eventName.value = 'start'
let difftime = new Date().getTime() - dubleTime.value;
if (difftime > 0 && difftime <= props.dbClickDiff) {
/**
* 双击事件
* @param evt {object} {x,y,type,width,height}
*/
emit("doubleClick", { x: _start_x.value, y: _start_y.value, type: 'doubleClick', width: rectBox.width, height: rectBox.height })
eventName.value = 'doubleClick'
}
dubleTime.value = new Date().getTime();
if (evt.changedTouches.length >= 2) {
pinth_x.value = evt.touches[0].pageX;
pinth_y.value = evt.touches[0].pageY;
let otx = evt.touches[1].pageX;
let oty = evt.touches[1].pageY;
let preVs = { x: otx - pinth_x.value, y: oty - pinth_y.value } as CHECKPOINT_XY;
preV.value = preVs
pinchStartLen.value = getLen(preVs);
}
tid.value = setTimeout(function () {
/**
* 长按事件
* @param evt {object} {x,y,type,width,height}
*/
emit("longPress", { x: _start_x.value, y: _start_y.value, type: 'longPress', width: rectBox.width, height: rectBox.height })
eventName.value = 'longPress'
}, props.longDiff);
}
// 键盘事件处理(无障碍访问)
// #ifdef WEB
const handleKeyDown = (evt: KeyboardEvent) => {
if (!props.accessibility || _disabled.value) return
const rectBox = parentRect.value
const centerX = rectBox.width / 2
const centerY = rectBox.height / 2
switch (evt.key) {
case 'Enter':
case ' ':
evt.preventDefault()
emit("click", {
x: centerX,
y: centerY,
type: 'click',
width: rectBox.width,
height: rectBox.height
})
break
case 'ArrowUp':
evt.preventDefault()
emit("swiper", {
x: centerX,
y: centerY,
diffX: 0,
diffY: props.swiperDiff,
direction: 'up',
type: 'swiper',
width: rectBox.width,
height: rectBox.height
})
break
case 'ArrowDown':
evt.preventDefault()
emit("swiper", {
x: centerX,
y: centerY,
diffX: 0,
diffY: props.swiperDiff,
direction: 'down',
type: 'swiper',
width: rectBox.width,
height: rectBox.height
})
break
case 'ArrowLeft':
evt.preventDefault()
emit("swiper", {
x: centerX,
y: centerY,
diffX: props.swiperDiff,
diffY: 0,
direction: 'left',
type: 'swiper',
width: rectBox.width,
height: rectBox.height
})
break
case 'ArrowRight':
evt.preventDefault()
emit("swiper", {
x: centerX,
y: centerY,
diffX: props.swiperDiff,
diffY: 0,
direction: 'right',
type: 'swiper',
width: rectBox.width,
height: rectBox.height
})
break
}
}
// #endif
const mMove = (evt : UniTouchEvent) => {
if (_disabled.value) return
let event = evt.changedTouches[0]
let rectBox = parentRect.value
let x = event.clientX - rectBox.left;
let y = event.clientY - rectBox.top;
let deltaX = Math.abs(x - _x.value);
let deltaY = Math.abs(y - _y.value);
// 检查最小移动距离
if (!isGestureValid(deltaX, deltaY)) {
return
}
mX.value = Math.max(0, Math.min(rectBox.width, x)) - _start_x.value
mY.value = Math.max(0, Math.min(rectBox.height, y)) - _start_y.value
// 在移动过程中实时更新累积坐标(用于插槽输出)
accumulatedX.value = startAccumulatedX.value + mX.value
accumulatedY.value = startAccumulatedY.value + mY.value
if (deltaX > deltaY && deltaX > props.swiperDiff) {
swipeDirection.value = (_x.value > x) ? "left" : "right";
} else if (deltaY > deltaX && deltaY > props.swiperDiff) {
swipeDirection.value = (_y.value < y) ? "down" : "up";
}
if (swipeDirection.value != "") {
/**
* 滑动时触发
* @param evt {object} {x,y,type,width,height,direction:方向UP|DOWN|LEFT|RIGHT}
*/
emit("swiper", { x: mX.value, y: mY.value, diffX: deltaX, diffY: deltaY, direction: swipeDirection.value, type: 'swiper', width: rectBox.width, height: rectBox.height })
eventName.value = 'swiper'
}
// _x.value = x;
// _y.value = y;
/**
* 触摸移动时触发
* @param evt {object} {x,y,type,width,height}
*/
emit("move", { x: mX.value, y: mY.value, type: 'move', width: rectBox.width, height: rectBox.height })
eventName.value = 'move'
clearTimeout(tid.value)
if (evt.changedTouches.length >= 2) {
let currentX = evt.touches[0].pageX;
let currentY = evt.touches[0].pageY;
let otx = evt.touches[1].pageX;
let oty = evt.touches[1].pageY;
let v = { x: otx - currentX, y: oty - currentY } as CHECKPOINT_XY;
if (preV.value.x !== null) {
let nowLenPitch = getLen(v);
if (pinchStartLen.value > 0) {
let temsc = (nowLenPitch / pinchStartLen.value);
// 计算缩放比例
const deltaScale = (temsc - 1) * zoomFactor.value + scale.value;
// 最小值为0.1
scale.value = Math.max(deltaScale, 0.1)
/**
* 缩放事件
* @param evt {object} {x,y,x1,y1,len,scale,type,width,height}
* @description len为两点间的距离,scale为当前的缩放比例(最小为0.1)
*/
emit("pinch", {
x: currentX, y: currentY,
x1: otx, y2: oty,
type: 'pinch',
width: rectBox.width, height: rectBox.height,
len: nowLenPitch,
scale: scale.value
})
}
let testjd = getRotateAngle(v, preV.value);
angle.value = Math.floor((testjd - 1) * zoomFactorAb.value) + angle.value;
pinchStartLen.value = nowLenPitch;
/**
* 旋转事件
* @param evt {object} {x,y,x1,y1,len,angle,type,width,height}
* @description len为两点间的距离,angle为当前当前旋转的角度
*/
emit("rotate", {
x: currentX, y: currentY,
x1: otx, y2: oty,
type: 'rotate',
width: rectBox.width, height: rectBox.height,
len: nowLenPitch,
angle: angle.value
})
}
preV.value = v;
}
}
const mEnd = (evt : UniTouchEvent) => {
clearTimeout(tid.value)
getRectBox((rect:DRect)=>{
parentRect.value = rect
})
if (_disabled.value) return
let event = evt.changedTouches[0]
let rectBox = parentRect.value
let x = event.clientX - rectBox.left;
let y = event.clientY - rectBox.top;
mX.value = Math.max(0, Math.min(rectBox.width, x)) - _start_x.value
mY.value = Math.max(0, Math.min(rectBox.height, y)) - _start_y.value
/**
* 触摸结束
* @param evt {object} {x,y,type,width,height}
*/
emit("end", { x: mX.value, y: mY.value, type: 'end', width: rectBox.width, height: rectBox.height })
eventName.value = 'end'
if (new Date().getTime() - dubleTime.value > props.clickDiff) {
/**
* 单击
* @param evt {object} {x,y,type,width,height}
*/
emit("click", { x: mX.value, y: mY.value, type: 'click', width: rectBox.width, height: rectBox.height })
eventName.value = 'click'
}
// 重置所有状态,为下次触摸做准备
preV.value = { x: 0, y: 0 } as CHECKPOINT_XY;
pinchStartLen.value = 0
swipeDirection.value = ""
scale.value = 0
angle.value = 0
}
const mCancel = (evt : UniTouchEvent) => {
clearTimeout(tid.value)
getRectBox((rect:DRect)=>{
parentRect.value = rect
})
if (_disabled.value) return
let event = evt.changedTouches[0]
let rectBox = parentRect.value
let x = event.clientX - rectBox.left;
let y = event.clientY - rectBox.top;
mX.value = Math.max(0, Math.min(rectBox.width, x)) - _start_x.value
mY.value = Math.max(0, Math.min(rectBox.height, y)) - _start_y.value
// 更新累积坐标(手势取消时也要累加本次移动的偏移量)
accumulatedX.value = startAccumulatedX.value + mX.value
accumulatedY.value = startAccumulatedY.value + mY.value
/**
* 触摸结束
* @param evt {object} {x,y,type,width,height}
*/
emit("cancel", { x: mX.value, y: mY.value, type: 'end', width: rectBox.width, height: rectBox.height })
eventName.value = 'cancel'
// 重置所有状态,为下次触摸做准备
preV.value = { x: 0, y: 0 } as CHECKPOINT_XY;
pinchStartLen.value = 0
swipeDirection.value = ""
scale.value = 0
angle.value = 0
}
// Methods
const initFunc = () => {
getRectBox((rect:DRect)=>{
parentRect.value = rect;
top.value = rect.top;
left.value = rect.left;
})
}
// Lifecycle
onMounted(() => {
// #ifdef APP-HARMONY
setTimeout(function() {
initFunc()
}, 120);
// #endif
// #ifndef APP-HARMONY
initFunc()
// #endif
})
onBeforeUnmount(() => {
clearTimeout(tid.value)
if (throttleTimer.value!=null) {
clearTimeout(throttleTimer.value!)
}
if (debounceTimer.value!=null) {
clearTimeout(debounceTimer.value!)
}
})
</script>
<template>
<view :id="id" ref="xFingerRef" class="finger"
@touchstart="mStart" @touchmove="mMove" @touchend="mEnd"
@touchcancel="mCancel"
:aria-label="props.ariaLabel"
:role="props.accessibility ? 'button' : null"
:tabindex="props.accessibility ? 0 : null"
<!-- #ifdef WEB -->
@mousedown="mouseStart"
@mousemove="mouseMove"
@mouseup="mouseEnd"
@mouseleave="mouseCancel"
@wheel="handleWheel"
@keydown="handleKeyDown"
<!-- #endif -->
>
<!--
@slot 默认插槽
@prop {number} x - 触摸的位置x
@prop {number} y - 触摸的位置y
@prop {number} accX - 累积x坐标(连续定位)
@prop {number} accY - 累积y坐标(连续定位)
@prop {string} type - 事件类型click,start,end,dbclick,longPress,swiper,pinch,rotate
-->
<slot :x="mX" :y="mY" :accX="accumulatedX" :accY="accumulatedY" :type="eventName"></slot>
</view>
</template>
<style lang="scss">
</style>