Bilibili-Evolved/registry/lib/components/touch/mini-player/touch-move.ts

156 lines
4.6 KiB
TypeScript

import { getComponentSettings } from '@/core/settings'
interface Point {
x: number
y: number
}
interface Position {
screenX: number
screenY: number
clientX: number
clientY: number
}
const isValidMove = (startPoint: Point, currentPoint: Point, minMoveDistance: number) => {
const x2 = Math.abs(startPoint.x - currentPoint.x) ** 2
const y2 = Math.abs(startPoint.y - currentPoint.y) ** 2
return x2 + y2 >= minMoveDistance * minMoveDistance
}
const composeMouseEvent = (type: string, position: Position) =>
new MouseEvent(type, {
screenX: position.screenX,
screenY: position.screenY,
clientX: position.clientX,
clientY: position.clientY,
bubbles: true,
cancelable: true,
view: unsafeWindow,
detail: 1,
})
// const composeWheelEvent = (startPoint: Point, currentPoint: Point, position: Position) => {
// return new WheelEvent('wheel', {
// deltaY: currentPoint.y - startPoint.y,
// screenX: position.screenX,
// screenY: position.screenY,
// clientX: position.clientX,
// clientY: position.clientY,
// bubbles: true,
// cancelable: true,
// view: unsafeWindow,
// })
// }
type TouchEventHandler = (e: TouchEvent) => void
interface TouchEventHandlers {
element: HTMLElement
touchstart: TouchEventHandler
touchmove: TouchEventHandler
touchend: TouchEventHandler
// [key: string]: any
}
const attachedHandlers: TouchEventHandlers[] = []
const listenerOptions = { passive: false, capture: true }
/** 触摸拖动选项 */
export interface TouchMoveOptions {
/** 最小移动距离(px), 大于这个值才会视为拖动操作 */
minMoveDistance?: number
// /** */
// scroll?: boolean
}
/**
* 为元素启用触摸拖动支持(模拟鼠标事件), 元素本身需要支持用鼠标拖动
* @param element 目标元素
* @param options 选项
*/
export const enableTouchMove = (element: HTMLElement, options?: TouchMoveOptions) => {
if (attachedHandlers.some(h => h.element === element)) {
return
}
let startPoint: Point
// let startTouch: Touch
let lastTouch: Touch
let move: boolean
const minMoveDistance = lodash.get(
options,
'minMoveDistance',
getComponentSettings('touchMiniPlayer').options.touchMoveDistance,
) as number
// const scroll = lodash.get(options, 'scroll', false)
const touchstart = (e: TouchEvent) => {
if (e.touches.length < 1) {
return
}
const touch = e.touches[0]
// startTouch = touch
startPoint = {
x: touch.clientX,
y: touch.clientY,
}
;(e.target as HTMLElement).dispatchEvent(composeMouseEvent('mousedown', touch))
}
element.addEventListener('touchstart', touchstart, listenerOptions)
const touchmove = (e: TouchEvent) => {
if (e.touches.length !== 1) {
return
}
const touch = e.touches[0]
const currentPoint = {
x: touch.clientX,
y: touch.clientY,
}
// if (!scroll) {
if (isValidMove(startPoint, currentPoint, minMoveDistance)) {
;(e.target as HTMLElement).dispatchEvent(composeMouseEvent('mousemove', touch))
move = true
if (e.cancelable) {
e.preventDefault()
}
} else {
move = false
}
// } else {
// const lastPoint = {
// x: lastTouch ? lastTouch.clientX : startPoint.x,
// y: lastTouch ? lastTouch.clientY : startPoint.y,
// }
// console.log('touch scroll', lastPoint, currentPoint);
// (e.target as HTMLElement).dispatchEvent(
// composeWheelEvent(lastPoint, currentPoint, startTouch)
// )
// }
lastTouch = touch
}
element.addEventListener('touchmove', touchmove, listenerOptions)
const touchend = (e: TouchEvent) => {
if (move) {
;(e.target as HTMLElement).dispatchEvent(composeMouseEvent('mouseup', lastTouch))
if (e.cancelable) {
e.preventDefault()
}
move = false
}
}
element.addEventListener('touchend', touchend, listenerOptions)
element.addEventListener('touchcancel', touchend, listenerOptions)
attachedHandlers.push({
element,
touchstart,
touchmove,
touchend,
})
}
/**
* 取消由`enableTouchMove`建立的触摸事件
* @param element 目标元素
*/
export const disableTouchMove = (element: HTMLElement) => {
const handlersIndex = attachedHandlers.findIndex(h => h.element === element)
if (handlersIndex === -1) {
return
}
const handlers = attachedHandlers[handlersIndex]
;['touchstart', 'touchmove', 'touchend'].forEach(event => {
element.removeEventListener(event, handlers[event], listenerOptions)
})
element.removeEventListener('touchcancel', handlers.touchend, listenerOptions)
attachedHandlers.splice(handlersIndex, 1)
}