浏览代码

fix: Fix Resizable not working on touch screen

zhangyumei.0319 8 月之前
父节点
当前提交
3c4e287508

+ 21 - 9
packages/semi-foundation/resizable/group/index.ts

@@ -1,6 +1,6 @@
 import { getItemDirection, getPixelSize } from "../utils";
 import BaseFoundation, { DefaultAdapter } from '../../base/foundation';
-import { ResizeStartCallback, ResizeCallback } from "../types";
+import { ResizeStartCallback, ResizeCallback, ResizeEventType } from "../types";
 import { adjustNewSize, judgeConstraint, getOffset } from "../utils";
 import { debounce } from "lodash";
 export interface ResizeHandlerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
@@ -49,8 +49,8 @@ export interface ResizeGroupAdapter<P = Record<string, any>, S = Record<string,
     getItemChange: (index: number) => ResizeCallback;
     getItemEnd: (index: number) => ResizeCallback;
     getItemDefaultSize: (index: number) => string | number;
-    registerEvents: () => void;
-    unregisterEvents: () => void
+    registerEvents: (type: ResizeEventType) => void;
+    unregisterEvents: (type: ResizeEventType) => void
 }
 
 export class ResizeGroupFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ResizeGroupAdapter<P, S>, P, S> {
@@ -72,6 +72,7 @@ export class ResizeGroupFoundation<P = Record<string, any>, S = Record<string, a
     itemMinusMap: Map<number, number>; // 这个是为了给handler留出空间,方便维护每一个item的size为cal(percent% - minus)
     totalMinus: number;
     itemPercentMap: Map<number, number>; // 内部维护一个百分比数组,消除浮点计算误差
+    type?: ResizeEventType;
 
 
     init(): void {
@@ -86,14 +87,15 @@ export class ResizeGroupFoundation<P = Record<string, any>, S = Record<string, a
 
     
     registerEvents = () => {
-        this._adapter.registerEvents();
+        this._adapter.registerEvents(this.type);
     }
 
     unregisterEvents = () => {
-        this._adapter.unregisterEvents();
+        this._adapter.unregisterEvents(this.type);
     }
 
-    onResizeStart = (handlerIndex: number, e: MouseEvent) => { // handler ref
+    onResizeStart = (handlerIndex: number, e: MouseEvent | Touch, type: ResizeEventType) => { // handler ref
+        this.type = type;
         let { clientX, clientY } = e;
         let lastItem = this._adapter.getItem(handlerIndex), nextItem = this._adapter.getItem(handlerIndex + 1);
         let lastOffset: number, nextOffset: number;
@@ -135,15 +137,25 @@ export class ResizeGroupFoundation<P = Record<string, any>, S = Record<string, a
         }
     }
 
+    onMouseMove = (e: MouseEvent) => {
+        this.onResizing(e);
+    }
 
-    onResizing = (e: MouseEvent) => {
+    onTouchMove = (e: TouchEvent) => {
+        // prevent page move in mobile
+        e.preventDefault();
+        this.onResizing(e);
+    }
+
+
+    onResizing = (e: MouseEvent | TouchEvent) => {
         const state = this.getStates();
         if (!state.isResizing) {
             return;
         }
         const { curHandler, originalPosition } = state;
         let { x: initX, y: initY, lastItemSize, nextItemSize, lastOffset, nextOffset } = originalPosition;
-        let { clientX, clientY } = e;
+        let { clientX, clientY } = this.type === 'mouse' ? e : (e as any).targetTouches[0];
 
         const props = this.getProps();
         const { direction } = props;
@@ -193,7 +205,7 @@ export class ResizeGroupFoundation<P = Record<string, any>, S = Record<string, a
         }
     }
 
-    onResizeEnd = (e: MouseEvent) => {
+    onResizeEnd = (e: MouseEvent | TouchEvent) => {
         const { curHandler } = this.getStates();
         let lastItem = this._adapter.getItem(curHandler), nextItem = this._adapter.getItem(curHandler + 1);
         let lastFunc = this._adapter.getItemEnd(curHandler),

+ 25 - 8
packages/semi-foundation/resizable/single/index.ts

@@ -1,5 +1,5 @@
 import BaseFoundation, { DefaultAdapter } from '../../base/foundation';
-import { DEFAULT_SIZE, Size, NumberSize, Direction, NewSize } from "../types";
+import { DEFAULT_SIZE, Size, NumberSize, Direction, NewSize, ResizeEventType } from "../types";
 import { getStringSize, getNumberSize, has, calculateNewMax, findNextSnap, snap, clamp } from "../utils";
 export interface ResizableHandlerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
     registerEvent: () => void;
@@ -16,9 +16,14 @@ export class ResizableHandlerFoundation<P = Record<string, any>, S = Record<stri
     }
 
     onMouseDown = (e: MouseEvent) => {
-        this.getProp('onResizeStart')(e, this.getProp('direction'));
+        this.getProp('onResizeStart')(e, this.getProp('direction'), 'mouse');
     };
 
+    onTouchStart = (e: TouchEvent) => {
+        const touch = e.targetTouches[0];
+        this.getProp('onResizeStart')(touch, this.getProp('direction'), 'touch');
+    }
+
     destroy(): void {
         this._adapter.unregisterEvent();
     }
@@ -26,8 +31,8 @@ export class ResizableHandlerFoundation<P = Record<string, any>, S = Record<stri
 
 export interface ResizableAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
     getResizable: () => HTMLDivElement | null;
-    registerEvent: () => void;
-    unregisterEvent: () => void
+    registerEvent: (type: ResizeEventType) => void;
+    unregisterEvent: (type: ResizeEventType) => void
 }
 
 export class ResizableFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ResizableAdapter<P, S>, P, S> {
@@ -53,6 +58,7 @@ export class ResizableFoundation<P = Record<string, any>, S = Record<string, any
     }
 
     flexDirection?: 'row' | 'column';
+    type?: ResizeEventType;
 
     lockAspectRatio = 1;
     resizable: HTMLElement | null = null;
@@ -202,11 +208,11 @@ export class ResizableFoundation<P = Record<string, any>, S = Record<string, any
     }
 
     registerEvents() {
-        this._adapter.registerEvent();
+        this._adapter.registerEvent(this.type);
     }
 
     unregisterEvents() {
-        this._adapter.unregisterEvent();
+        this._adapter.unregisterEvent(this.type);
     }
 
     getCssPropertySize(newSize: number | string, property: 'width' | 'height'): number | string {
@@ -380,7 +386,8 @@ export class ResizableFoundation<P = Record<string, any>, S = Record<string, any
     }
 
 
-    onResizeStart = (e: MouseEvent, direction: Direction) => {
+    onResizeStart = (e: MouseEvent, direction: Direction, type: ResizeEventType) => {
+        this.type = type;
         this.resizable = this._adapter.getResizable();
         if (!this.resizable || !this.window) {
             return;
@@ -456,6 +463,16 @@ export class ResizableFoundation<P = Record<string, any>, S = Record<string, any
 
 
     onMouseMove = (event: MouseEvent) => {
+        this.changePosition(event);
+    }
+
+    onTouchMove = (event: TouchEvent) => {
+        event.preventDefault();
+        const touch = event.targetTouches[0];
+        this.changePosition(touch);
+    }
+
+    changePosition = (event: Touch | MouseEvent) => {
         const states = this.getStates();
         const props = this.getProps();
 
@@ -583,7 +600,7 @@ export class ResizableFoundation<P = Record<string, any>, S = Record<string, any
     }
 
 
-    onMouseUp = (event: MouseEvent) => {
+    onMouseUp = (event: MouseEvent | TouchEvent) => {
         const { isResizing, direction, original } = this.getStates();
 
         if (!isResizing || !this.resizable) {

+ 6 - 3
packages/semi-foundation/resizable/types.ts

@@ -14,9 +14,12 @@ export interface HandleClassName {
     topLeft?: string
 }
 
+export type ResizeEventType = 'mouse' | 'touch'; 
+
 export type HandlerCallback = (
     e: MouseEvent,
-    direction: Direction
+    direction: Direction,
+    type?: ResizeEventType,
 ) => void;
 
 export interface Enable {
@@ -51,12 +54,12 @@ export const DEFAULT_SIZE = {
 
 export type ResizeCallback = (
     size: Size,
-    event: MouseEvent,
+    event: MouseEvent | TouchEvent,
     direction: Direction,
 ) => void;
 
 export type ResizeStartCallback = (
-    e: MouseEvent,
+    e: MouseEvent | Touch,
     dir: Direction,
 ) => void | boolean;
 

+ 2 - 2
packages/semi-ui/resizable/group/resizeContext.ts

@@ -1,5 +1,5 @@
 import React, { createContext, RefObject } from 'react';
-import { ResizeCallback, ResizeStartCallback } from '@douyinfe/semi-foundation/resizable/types';
+import { ResizeCallback, ResizeEventType, ResizeStartCallback } from '@douyinfe/semi-foundation/resizable/types';
 
 export interface ResizeContextProps {
     direction: 'horizontal' | 'vertical';
@@ -10,7 +10,7 @@ export interface ResizeContextProps {
         onResizeEnd: ResizeCallback
     ) => number;
     registerHandler: (ref: RefObject<HTMLDivElement>) => number;
-    notifyResizeStart: (handlerIndex: number, e: MouseEvent) => void;
+    notifyResizeStart: (handlerIndex: number, e: MouseEvent | Touch, type: ResizeEventType) => void;
     getGroupSize: () => number
 }
 

+ 21 - 9
packages/semi-ui/resizable/group/resizeGroup.tsx

@@ -5,7 +5,7 @@ import { ResizeGroupFoundation, ResizeGroupAdapter } from '@douyinfe/semi-founda
 import { cssClasses } from '@douyinfe/semi-foundation/resizable/constants';
 import BaseComponent from '../../_base/baseComponent';
 import { ResizeContext, ResizeContextProps } from './resizeContext';
-import { ResizeCallback, ResizeStartCallback } from '@douyinfe/semi-foundation/resizable/types';
+import { ResizeCallback, ResizeEventType, ResizeStartCallback } from '@douyinfe/semi-foundation/resizable/types';
 import "@douyinfe/semi-foundation/resizable/resizable.scss";
 
 const prefixCls = cssClasses.PREFIX;
@@ -145,19 +145,31 @@ class ResizeGroup extends BaseComponent<ResizeGroupProps, ResizeGroupState> {
         return this.groupRef.current.ownerDocument.defaultView as Window ?? null;
     }
 
-    registerEvent = () => {
+    registerEvent = (type: ResizeEventType = 'mouse') => {
         if (this.window) {
-            this.window.addEventListener('mousemove', this.foundation.onResizing);
-            this.window.addEventListener('mouseup', this.foundation.onResizeEnd);
-            this.window.addEventListener('mouseleave', this.foundation.onResizeEnd);
+            if (type === 'mouse') {
+                this.window.addEventListener('mousemove', this.foundation.onMouseMove);
+                this.window.addEventListener('mouseup', this.foundation.onResizeEnd);
+                this.window.addEventListener('mouseleave', this.foundation.onResizeEnd);
+            } else {
+                this.window.addEventListener('touchmove', this.foundation.onTouchMove, { passive: false });
+                this.window.addEventListener('touchend', this.foundation.onResizeEnd);
+                this.window.addEventListener('touchcancel', this.foundation.onResizeEnd);
+            } 
         }
     }
 
-    unregisterEvent = () => {
+    unregisterEvent = (type: ResizeEventType = 'mouse') => {
         if (this.window) {
-            this.window.removeEventListener('mousemove', this.foundation.onResizing);
-            this.window.removeEventListener('mouseup', this.foundation.onResizeEnd);
-            this.window.removeEventListener('mouseleave', this.foundation.onResizeEnd);
+            if (type === 'mouse') {
+                this.window.removeEventListener('mousemove', this.foundation.onMouseMove);
+                this.window.removeEventListener('mouseup', this.foundation.onResizeEnd);
+                this.window.removeEventListener('mouseleave', this.foundation.onResizeEnd);
+            } else {
+                this.window.removeEventListener('touchmove', this.foundation.onTouchMove, { passive: false } as any);
+                this.window.removeEventListener('touchend', this.foundation.onResizeEnd);
+                this.window.removeEventListener('touchcancel', this.foundation.onResizeEnd);
+            }
         }
     }
 

+ 8 - 1
packages/semi-ui/resizable/group/resizeHandler.tsx

@@ -62,7 +62,12 @@ class ResizeHandler extends BaseComponent<ResizeHandlerProps, ResizeHandlerState
     foundation: ResizeHandlerFoundation;
     onMouseDown = (e: MouseEvent) => {
         const { notifyResizeStart } = this.context;
-        notifyResizeStart(this.handlerIndex, e);
+        notifyResizeStart(this.handlerIndex, e, 'mouse');
+    }
+
+    onTouchStart = (e: TouchEvent) => {
+        const { notifyResizeStart } = this.context;
+        notifyResizeStart(this.handlerIndex, e.targetTouches[0], 'touch');
     }
     
     get adapter(): ResizeHandlerAdapter<ResizeHandlerProps, ResizeHandlerState> {
@@ -70,9 +75,11 @@ class ResizeHandler extends BaseComponent<ResizeHandlerProps, ResizeHandlerState
             ...super.adapter,
             registerEvents: () => {
                 this.handlerRef.current.addEventListener('mousedown', this.onMouseDown);
+                this.handlerRef.current.addEventListener('touchstart', this.onTouchStart);
             },
             unregisterEvents: () => {
                 this.handlerRef.current.removeEventListener('mousedown', this.onMouseDown);
+                this.handlerRef.current.removeEventListener('touchstart', this.onTouchStart);
             },
         };
     }

+ 20 - 8
packages/semi-ui/resizable/single/resizable.tsx

@@ -185,17 +185,29 @@ class Resizable extends BaseComponent<ResizableProps, ResizableState> {
         return {
             ...super.adapter,
             getResizable: this.getResizable,
-            registerEvent: () => {
+            registerEvent: (type = 'mouse') => {
                 let window = this.foundation.window;
-                window?.addEventListener('mouseup', this.foundation.onMouseUp);
-                window?.addEventListener('mousemove', this.foundation.onMouseMove);
-                window?.addEventListener('mouseleave', this.foundation.onMouseUp);
+                if (type === 'mouse') {
+                    window?.addEventListener('mouseup', this.foundation.onMouseUp);
+                    window?.addEventListener('mousemove', this.foundation.onMouseMove);
+                    window?.addEventListener('mouseleave', this.foundation.onMouseUp);
+                } else {
+                    window?.addEventListener('touchmove', this.foundation.onTouchMove, { passive: false });
+                    window?.addEventListener('touchend', this.foundation.onMouseUp);
+                    window?.addEventListener('touchcancel', this.foundation.onMouseUp);
+                }
             },
-            unregisterEvent: () => {
+            unregisterEvent: (type = 'mouse') => {
                 let window = this.foundation.window;
-                window?.removeEventListener('mouseup', this.foundation.onMouseUp);
-                window?.removeEventListener('mousemove', this.foundation.onMouseMove);
-                window?.removeEventListener('mouseleave', this.foundation.onMouseUp);
+                if (type === 'mouse') {
+                    window?.removeEventListener('mouseup', this.foundation.onMouseUp);
+                    window?.removeEventListener('mousemove', this.foundation.onMouseMove);
+                    window?.removeEventListener('mouseleave', this.foundation.onMouseUp);
+                } else {
+                    window?.removeEventListener('touchmove', this.foundation.onTouchMove, { passive: false } as any);
+                    window?.removeEventListener('touchend', this.foundation.onMouseUp);
+                    window?.removeEventListener('touchcancel', this.foundation.onMouseUp);
+                }
             },
         };
     }

+ 2 - 0
packages/semi-ui/resizable/single/resizableHandler.tsx

@@ -62,9 +62,11 @@ class ResizableHandler extends BaseComponent<ResizableHandlerProps, ResizableHan
             ...super.adapter,
             registerEvent: () => {
                 this.resizeHandlerRef.current.addEventListener('mousedown', this.foundation.onMouseDown);
+                this.resizeHandlerRef.current.addEventListener('touchstart', this.foundation.onTouchStart);
             },
             unregisterEvent: () => {
                 this.resizeHandlerRef.current.removeEventListener('mousedown', this.foundation.onMouseDown);
+                this.resizeHandlerRef.current.removeEventListener('touchstart', this.foundation.onTouchStart);
             },
         };
     }