import { getDirection, now } from "./uitls" export const touchMixin = function() { return { data() { return { tonchOpt: { direction: '', //滑动方向 startX: '', //开始滑动的x坐标 startY: '', //开始滑动的y坐标 nextIndex: -1, //下一个切换的标签下标 moved: false, //是否为一次水平滑动 deltaX: 0, deltaY: 0, startTimestamp: 0, nestedSwipeable: true, //对于嵌套的上级tabs,是否允许水平滑动(下级tabs滑动边界时才允许上级tabs滑动) } }; }, computed: { // 是否允许水平滑动 horizontalSwipe() { return this.swipeable && this.tonchOpt.nestedSwipeable } }, created() { this.nestedTabs = this.getNestedTabs(); }, methods: { //获取嵌套的上级tabs实例 getNestedTabs(name = 'y-tabs') { let parent = this.$parent; let parentName = parent.$options.name; while (parentName !== name) { parent = parent.$parent; if (!parent) return false; parentName = parent.$options.name; } return parent; }, touchStart(event) { this.releaseScrollspyLock(); //释放滚动锁 if (!this.horizontalSwipe) return; this.resetTouchStatus(); this.tonchOpt.startX = event.touches[0].clientX; this.tonchOpt.startY = event.touches[0].clientY; this.tonchOpt.startTimestamp = now(); }, touchMove(event) { if (!this.horizontalSwipe) return; const touch = event.touches[0]; this.tonchOpt.deltaX = touch.clientX < 0 ? 0 : this.tonchOpt.startX - touch.clientX; this.tonchOpt.deltaY = this.tonchOpt.startY - touch.clientY; const offsetX = Math.abs(this.tonchOpt.deltaX); const offsetY = Math.abs(this.tonchOpt.deltaY); // 当距离大于某个值时锁定方向 if (!this.tonchOpt.direction || (offsetX < 10 && offsetY < 10)) { this.tonchOpt.direction = getDirection(offsetX, offsetY); } if (this.tonchOpt.direction === "horizontal") { //水平滑动 const { dataLen, contentWidth, currentIndex, tabs, swipeAnimated } = this; const isRight = this.tonchOpt.deltaX < 0; //判断是否向右滑动 // 如果为第一页,则不允许向右滑;为最后一页,则不允许左滑 if ((isRight && currentIndex === 0) || (!isRight && currentIndex === dataLen - 1)) { this.setNestTabsSwipe(true) return; } else { this.setNestTabsSwipe(false) } this.tonchOpt.nextIndex = currentIndex + (isRight ? -1 : 1); //下一个标签 if (tabs[this.tonchOpt.nextIndex]?.disabled) return; //禁用的标签不允许滑动 this.tonchOpt.moved = true; //标记为一次水平滑动 // 改变标签内容滑动轨道样式,模拟拖动动画效果 if (swipeAnimated) { const offsetWidth = contentWidth * currentIndex * -1 + offsetX * (isRight ? 1 : -1); this.changeTrackStyle(true, 0, offsetWidth); this.setDx(this.tonchOpt.deltaX, false); } event.preventDefault(); // 左右滑动时阻止事件继续向上传递,避免触发上下滑动(H5端生效) } }, touchEnd() { if (this.tonchOpt.moved) { // 何时可切换标签,当横向滑动距离大于设定阈值,或快速滑动(300ms内)切滑动距离大于18px时 const deltaTime = now() - this.tonchOpt.startTimestamp; const distance = Math.abs(this.tonchOpt.deltaX); const speed = (distance / deltaTime).toFixed(4); const isChange = speed > 0.25 || distance >= Number(this.swipeThreshold); //是否切换 const currIndex = this.currentIndex; //当前选中下标 const targetIndex = isChange ? this.tonchOpt.nextIndex : currIndex; //目标标签的下标 this.touchEndForPane(this.tonchOpt.deltaX, currIndex, targetIndex, isChange); } }, // 重置触摸状态 resetTouchStatus() { this.tonchOpt.direction = ''; this.tonchOpt.deltaX = 0; this.tonchOpt.deltaY = 0; this.tonchOpt.nextIndex = -1; this.tonchOpt.moved = false; this.tonchOpt.startTimestamp = 0; this.setNestTabsSwipe(true) }, // 设置嵌套的上级tabs是否可水平滑动 setNestTabsSwipe(value) { if (!this.nestedTabs) return; this.nestedTabs.tonchOpt.nestedSwipeable = value } } } }