|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
export type GET_MOVE_DIRECTION_INFO = { moveX: number; moveY: number; }; export type GET_MOVE_DIRECTION = { dom?: string | Element; notEdge?: boolean | null; edgeStop?: boolean | null; /** 为 true 时 callback 第二个参数返回额外信息 */ otherInfo?: boolean | null; callback: (direction: string, info?: GET_MOVE_DIRECTION_INFO) => void; }; /** * @Function 获取指定 dom 的 touchmove 方向 * @props { * dom: 指定 dom(选择器或元素) * notEdge: 不达到边缘就触发回调,默认 false * edgeStop: 到达边缘时是否禁止橡皮筋效果,默认 false * otherInfo: 是否返回额外信息,默认 false * callback: 回调函数,返回方向;otherInfo 为 true 时额外返回 { moveX, moveY } * } */ export class GetMoveDirection { startX: number; startY: number; dom: Element | Document | null; props: GET_MOVE_DIRECTION; private listenerOptions: AddEventListenerOptions; constructor(props: GET_MOVE_DIRECTION) { this.startX = 0; this.startY = 0; if (!props?.dom) { this.dom = document; } else if (typeof props.dom === "string") { this.dom = document?.querySelector(props.dom); } else { this.dom = props.dom; } this.removeAllListent = this.removeAllListent.bind(this); this.listenTouchstart = this.listenTouchstart.bind(this); this.listenTouchmove = this.listenTouchmove.bind(this); this.props = props; // notEdge:捕获阶段,避免子元素 stopPropagation 拦掉 callback // edgeStop:passive:false,才能在边缘 preventDefault 禁用橡皮筋效果 this.listenerOptions = { capture: !!props?.notEdge, passive: !props?.edgeStop, }; this.dom?.addEventListener( "touchstart", this.listenTouchstart, this.listenerOptions ); this.dom?.addEventListener( "touchmove", this.listenTouchmove, this.listenerOptions ); } private getScrollDom(): HTMLElement | null { if (!this.dom) return null; return this.dom instanceof Document ? document.documentElement : (this.dom as HTMLElement); } private isAtLeftEdge(dom: HTMLElement) { return dom.scrollLeft === 0; } private isAtRightEdge(dom: HTMLElement) { return ( Math.ceil(dom.scrollLeft + dom.offsetWidth) >= dom.scrollWidth && Math.ceil(dom.scrollLeft + dom.clientWidth) >= dom.scrollWidth ); } private isAtTopEdge(dom: HTMLElement) { return dom.scrollTop === 0; } private isAtBottomEdge(dom: HTMLElement) { return ( Math.ceil(dom.scrollTop + dom.offsetHeight) >= dom.scrollHeight && Math.ceil(dom.scrollTop + dom.clientHeight) >= dom.scrollHeight ); } private applyEdgeStop(ev: TouchEvent, atEdge: boolean, axis: "x" | "y") { if (!this.props?.edgeStop || !atEdge) return; ev.cancelable && ev.preventDefault(); if (axis === "y") { ev.stopPropagation(); } } private emitDirection(direction: string, info: GET_MOVE_DIRECTION_INFO) { if (this.props?.otherInfo) { this.props.callback(direction, info); } else { this.props.callback(direction); } } listenTouchstart(ev: any) { this.startX = ev?.changedTouches?.[0]?.pageX; this.startY = ev?.changedTouches?.[0]?.pageY; } listenTouchmove(ev: any) { const moveEndX = ev?.changedTouches?.[0]?.pageX; const moveEndY = ev?.changedTouches?.[0]?.pageY; const moveX = moveEndX - this.startX; const moveY = moveEndY - this.startY; const _dom = this.getScrollDom(); if (!_dom) return; const absX = Math.abs(moveX); const absY = Math.abs(moveY); const info: GET_MOVE_DIRECTION_INFO = { moveX, moveY }; // 向右拖拽 if (absX > absY && moveX > 0) { const atEdge = this.isAtLeftEdge(_dom); if (this.props?.notEdge || atEdge) { this.emitDirection("right", info); } this.applyEdgeStop(ev, atEdge, "x"); } // 向左拖拽 if (absX > absY && moveX < 0) { const atEdge = this.isAtRightEdge(_dom); if (this.props?.notEdge || atEdge) { this.emitDirection("left", info); } this.applyEdgeStop(ev, atEdge, "x"); } // 向下拖拽 if (absY > absX && moveY > 0) { const atEdge = this.isAtTopEdge(_dom); if (this.props?.notEdge || atEdge) { this.emitDirection("down", info); } this.applyEdgeStop(ev, atEdge, "y"); } // 向上拖拽 if (absY > absX && moveY < 0) { const atEdge = this.isAtBottomEdge(_dom); if (this.props?.notEdge || atEdge) { this.emitDirection("up", info); } this.applyEdgeStop(ev, atEdge, "y"); } } removeAllListent() { this.dom?.removeEventListener( "touchstart", this.listenTouchstart, this.listenerOptions ); this.dom?.removeEventListener( "touchmove", this.listenTouchmove, this.listenerOptions ); } } |

