MinJieLiu há 5 anos atrás
pai
commit
7b96da0705

+ 17 - 13
src/PhotoSlider.tsx

@@ -6,7 +6,7 @@ import SlideWrap from './components/SlideWrap';
 import CloseSVG from './components/CloseSVG';
 import isMobile from './utils/isMobile';
 import './PhotoSlider.less';
-import { dataType, IPhotoProviderBase } from './types';
+import { dataType, IPhotoProviderBase, ReachTypeEnum } from './types';
 import { defaultOpacity, horizontalOffset, maxMoveOffset } from './variables';
 
 export interface IPhotoSliderProps extends IPhotoProviderBase {
@@ -127,7 +127,7 @@ export default class PhotoSlider extends React.Component<
     });
   };
 
-  handleReachVerticalMove = (_, clientY, scale) => {
+  handleReachVerticalMove = (clientY, scale) => {
     this.setState(({ lastClientY, backdropOpacity }) => {
       if (lastClientY === undefined) {
         return {
@@ -184,6 +184,19 @@ export default class PhotoSlider extends React.Component<
     }
   };
 
+  handleReachMove = (
+    reachState: ReachTypeEnum,
+    clientX: number,
+    clientY: number,
+    scale?: number,
+  ) => {
+    if (reachState === ReachTypeEnum.XReach) {
+      this.handleReachHorizontalMove(clientX);
+    } else if (reachState === ReachTypeEnum.YReach) {
+      this.handleReachVerticalMove(clientY, scale);
+    }
+  };
+
   handleReachUp = (clientX: number, clientY: number) => {
     const { innerWidth, innerHeight } = window;
     const { images, onIndexChange, onClose } = this.props;
@@ -307,20 +320,11 @@ export default class PhotoSlider extends React.Component<
                 <PhotoView
                   key={item.key || realIndex}
                   src={item.src}
-                  onReachTopMove={this.handleReachVerticalMove}
-                  onReachBottomMove={this.handleReachVerticalMove}
-                  onReachRightMove={
-                    realIndex < imageLength - 1
-                      ? this.handleReachHorizontalMove
-                      : undefined
-                  }
-                  onReachLeftMove={
-                    realIndex > 0 ? this.handleReachHorizontalMove : undefined
-                  }
+                  onReachMove={this.handleReachMove}
                   onReachUp={this.handleReachUp}
                   onPhotoTap={this.handlePhotoTap}
                   onMaskTap={this.handlePhotoMaskTap}
-                  wrapClassName={viewClassName}
+                  viewClassName={viewClassName}
                   className={imageClassName}
                   style={{
                     left: `${(innerWidth + horizontalOffset) * realIndex}px`,

+ 46 - 132
src/PhotoView.tsx

@@ -6,16 +6,16 @@ import isMobile from './utils/isMobile';
 import getMultipleTouchPosition from './utils/getMultipleTouchPosition';
 import getPositionOnMoveOrScale from './utils/getPositionOnMoveOrScale';
 import slideToPosition from './utils/slideToPosition';
-import { getClosedHorizontal, getClosedVertical } from './utils/getCloseEdge';
+import { getClosedHorizontal, getClosedVertical, getReachType } from './utils/getCloseEdge';
 import withContinuousTap, { TapFuncType } from './utils/withContinuousTap';
 import {
   maxScale,
-  minReachOffset,
   minStartTouchOffset,
   minScale,
   scaleBuffer,
 } from './variables';
 import {
+  ReachMoveFunction,
   ReachFunction,
   PhotoTapFunction,
   ReachTypeEnum,
@@ -28,7 +28,7 @@ export interface IPhotoViewProps {
   // 图片地址
   src: string;
   // 容器类名
-  wrapClassName?: string;
+  viewClassName?: string;
   // 图片类名
   className?: string;
   // style
@@ -39,19 +39,13 @@ export interface IPhotoViewProps {
   brokenElement?: JSX.Element;
 
   // Photo 点击事件
-  onPhotoTap?: PhotoTapFunction;
+  onPhotoTap: PhotoTapFunction;
   // Mask 点击事件
-  onMaskTap?: PhotoTapFunction;
-  // 到达顶部滑动事件
-  onReachTopMove?: ReachFunction;
-  // 到达右部滑动事件
-  onReachRightMove?: ReachFunction;
-  // 到达底部滑动事件
-  onReachBottomMove?: ReachFunction;
-  // 到达左部滑动事件
-  onReachLeftMove?: ReachFunction;
+  onMaskTap: PhotoTapFunction;
+  // 到达边缘滑动事件
+  onReachMove: ReachMoveFunction;
   // 触摸解除事件
-  onReachUp?: ReachFunction;
+  onReachUp: ReachFunction;
 
   onPhotoResize?: () => void;
 }
@@ -85,8 +79,6 @@ const initialState = {
 
   // 当前边缘触发状态
   reachState: ReachTypeEnum.Normal,
-  // 初始响应状态
-  initialTouchState: TouchStartEnum.Normal,
 };
 
 export default class PhotoView extends React.Component<
@@ -96,6 +88,9 @@ export default class PhotoView extends React.Component<
   static displayName = 'PhotoView';
 
   readonly state = initialState;
+  // 初始响应状态
+  private initialTouchState = TouchStartEnum.Normal;
+
   private readonly photoRef = React.createRef<Photo>();
   private readonly handlePhotoTap: TapFuncType<number>;
 
@@ -142,7 +137,6 @@ export default class PhotoView extends React.Component<
   };
 
   onMove = (newClientX: number, newClientY: number, touchLength: number = 0) => {
-    // minInitialTouchOffset
     const {
       x,
       y,
@@ -155,34 +149,19 @@ export default class PhotoView extends React.Component<
       reachState,
       touched,
       maskTouched,
-      initialTouchState,
     } = this.state;
     const { current } = this.photoRef;
-    let handleClientX = newClientX;
-    let handleClientY = newClientY;
     if ((touched || maskTouched) && current) {
-      // 最小缩放下,以初始移动距离来判断意图
-      if (scale === minScale && initialTouchState === TouchStartEnum.Normal) {
+      // 单指最小缩放下,以初始移动距离来判断意图
+      if (touchLength === 0 && scale === minScale && this.initialTouchState === TouchStartEnum.Normal) {
         const isBeyondX = Math.abs(newClientX - clientX) > minStartTouchOffset;
         const isBeyondY = Math.abs(newClientY - clientY) > minStartTouchOffset;
-
-        // 初始移动距离不足则不做操作
+        // 初始移动距离不足则不处理
         if (!(isBeyondX || isBeyondY)) {
           return;
         }
-
-        this.setState({
-          initialTouchState: isBeyondX ? TouchStartEnum.X : TouchStartEnum.Y,
-        });
-      }
-      if (initialTouchState === TouchStartEnum.X) {
-        handleClientX = newClientX - clientX > 0
-          ? newClientX + minStartTouchOffset
-          : newClientX - minStartTouchOffset;
-      } else {
-        handleClientY = newClientY - clientY > 0
-          ? newClientY - minStartTouchOffset
-          : newClientY + minStartTouchOffset;
+        // 设置响应状态
+        this.initialTouchState = isBeyondX ? TouchStartEnum.X : TouchStartEnum.Y;
       }
 
       const { width, height, naturalWidth } = current.state;
@@ -191,24 +170,23 @@ export default class PhotoView extends React.Component<
       // 边缘触发状态
       let currentReachState = ReachTypeEnum.Normal;
       if (touchLength === 0) {
-        const planX = handleClientX - clientX + lastX;
-        currentY = handleClientY - clientY + lastY;
+        const {
+          onReachMove,
+        } = this.props;
+        const planX = newClientX - clientX + lastX;
+        currentY = newClientY - clientY + lastY;
         // 边缘超出状态
         const horizontalCloseEdge = getClosedHorizontal(planX, scale, width);
         const verticalCloseEdge = getClosedVertical(currentY, scale, height);
         // X 方向响应距离减小
-        currentX = (handleClientX - clientX) / (horizontalCloseEdge !== CloseEdgeEnum.Normal ? 2 : 1)  + lastX;
+        currentX = (newClientX - clientX) / (horizontalCloseEdge !== CloseEdgeEnum.Normal ? 2 : 1)  + lastX;
         // 边缘触发检测
-        currentReachState = this.handleReachCallback({
-          x: planX,
-          y: currentY,
-          horizontalCloseEdge,
-          verticalCloseEdge,
-          clientX: handleClientX,
-          clientY: handleClientY,
-          scale,
-          reachState,
-        });
+        currentReachState = getReachType({ horizontalCloseEdge, verticalCloseEdge, reachState });
+
+        // 接触边缘
+        if (currentReachState != ReachTypeEnum.Normal) {
+          onReachMove(currentReachState, clientX, clientY, scale);
+        }
       }
       // 横向边缘触发、背景触发禁用当前滑动
       if (currentReachState === ReachTypeEnum.XReach || maskTouched) {
@@ -232,8 +210,8 @@ export default class PhotoView extends React.Component<
           ...getPositionOnMoveOrScale({
             x: currentX,
             y: currentY,
-            clientX: handleClientX,
-            clientY: handleClientY,
+            clientX: newClientX,
+            clientY: newClientY,
             fromScale: scale,
             toScale,
           }),
@@ -342,21 +320,24 @@ export default class PhotoView extends React.Component<
   };
 
   handleUp = (newClientX: number, newClientY: number) => {
-    const { touched, maskTouched } = this.state;
+    // 重置响应状态
+    this.initialTouchState = TouchStartEnum.Normal;
+    const {
+      x,
+      y,
+      lastX,
+      lastY,
+      scale,
+      touchedTime,
+      clientX,
+      clientY,
+      touched,
+      maskTouched,
+    } = this.state;
     const { current } = this.photoRef;
     if ((touched || maskTouched) && current) {
       const { onReachUp, onPhotoTap, onMaskTap } = this.props;
       const { width, height, naturalWidth } = current.state;
-      const {
-        x,
-        y,
-        lastX,
-        lastY,
-        scale,
-        touchedTime,
-        clientX,
-        clientY,
-      } = this.state;
       const hasMove = clientX !== newClientX || clientY !== newClientY;
       this.setState({
         touched: false,
@@ -367,7 +348,6 @@ export default class PhotoView extends React.Component<
           minScale,
         ),
         reachState: ReachTypeEnum.Normal, // 重置触发状态
-        initialTouchState: TouchStartEnum.Normal,
         ...hasMove
           ? slideToPosition({
             x,
@@ -416,76 +396,10 @@ export default class PhotoView extends React.Component<
     }
   };
 
-  handleReachCallback = ({
-    x,
-    y,
-    clientX,
-    clientY,
-    horizontalCloseEdge,
-    verticalCloseEdge,
-    scale,
-    reachState,
-  }: {
-    x: number,
-    y: number,
-    clientX: number,
-    clientY: number,
-    horizontalCloseEdge: CloseEdgeEnum,
-    verticalCloseEdge: CloseEdgeEnum,
-    scale: number,
-    reachState: ReachTypeEnum,
-  }): ReachTypeEnum => {
-    const {
-      onReachTopMove,
-      onReachRightMove,
-      onReachBottomMove,
-      onReachLeftMove,
-    } = this.props;
-    //  触碰到边缘
-    if (
-      onReachLeftMove
-      && (horizontalCloseEdge
-      && x > minReachOffset
-      && reachState === ReachTypeEnum.Normal
-      || reachState === ReachTypeEnum.XReach)
-    ) {
-      onReachLeftMove(clientX, clientY);
-      return ReachTypeEnum.XReach;
-    } else if (
-      onReachRightMove
-      && (horizontalCloseEdge
-      && x < -minReachOffset
-      && reachState === ReachTypeEnum.Normal
-      || reachState === ReachTypeEnum.XReach)
-    ) {
-      onReachRightMove(clientX, clientY);
-      return ReachTypeEnum.XReach;
-    } else if (
-      onReachTopMove
-      && (verticalCloseEdge
-      && y > minReachOffset
-      && reachState === ReachTypeEnum.Normal
-      || reachState === ReachTypeEnum.YReach)
-    ) {
-      onReachTopMove(clientX, clientY, scale);
-      return ReachTypeEnum.YReach;
-    } else if (
-      onReachBottomMove
-      && (verticalCloseEdge
-      && y < -minReachOffset
-      && reachState === ReachTypeEnum.Normal
-      || reachState === ReachTypeEnum.YReach)
-    ) {
-      onReachBottomMove(clientX, clientY, scale);
-      return ReachTypeEnum.YReach;
-    }
-    return ReachTypeEnum.Normal;
-  };
-
   render() {
     const {
       src,
-      wrapClassName,
+      viewClassName,
       className,
       style,
       loadingElement,
@@ -496,7 +410,7 @@ export default class PhotoView extends React.Component<
     const transform = `translate3d(${x}px, ${y}px, 0) scale(${scale})`;
 
     return (
-      <div className={classNames('PhotoView__PhotoWrap', wrapClassName)} style={style}>
+      <div className={classNames('PhotoView__PhotoWrap', viewClassName)} style={style}>
         <div
           className="PhotoView__PhotoMask"
           onMouseDown={isMobile ? undefined : this.handleMaskMouseDown}

+ 11 - 1
src/types.ts

@@ -52,7 +52,17 @@ export interface IPhotoProviderBase {
   brokenElement?: JSX.Element;
 }
 
-export type ReachFunction = (clientX: number, clientY: number, scale?: number) => void;
+export type ReachMoveFunction = (
+  reachState: ReachTypeEnum,
+  clientX: number,
+  clientY: number,
+  scale?: number,
+) => void;
+
+export type ReachFunction = (
+  clientX: number,
+  clientY: number,
+) => void;
 
 export type PhotoTapFunction = (clientX: number, clientY: number) => void;
 

+ 36 - 7
src/utils/getCloseEdge.ts

@@ -1,4 +1,4 @@
-import { CloseEdgeEnum } from '../types';
+import { CloseEdgeEnum, ReachTypeEnum } from '../types';
 
 /**
  * 接触左边或右边边缘
@@ -7,11 +7,11 @@ import { CloseEdgeEnum } from '../types';
  * @param width
  * @return CloseEdgeEnum
  */
-export const getClosedHorizontal = (
+export function getClosedHorizontal(
   x: number,
   scale: number,
   width: number,
-): CloseEdgeEnum => {
+): CloseEdgeEnum {
   const { innerWidth } = window;
   const currentWidth = width * scale;
   // 图片超出的宽度
@@ -24,7 +24,7 @@ export const getClosedHorizontal = (
     return CloseEdgeEnum.After;
   }
   return CloseEdgeEnum.Normal;
-};
+}
 
 /**
  * 接触上边或下边边缘
@@ -33,11 +33,11 @@ export const getClosedHorizontal = (
  * @param height
  * @return CloseEdgeEnum
  */
-export const getClosedVertical = (
+export function getClosedVertical(
   y: number,
   scale: number,
   height: number,
-): CloseEdgeEnum => {
+): CloseEdgeEnum {
   const { innerHeight } = window;
   const currentHeight = height * scale;
   // 图片超出的高度
@@ -51,4 +51,33 @@ export const getClosedVertical = (
     return CloseEdgeEnum.After;
   }
   return CloseEdgeEnum.Normal;
-};
+}
+
+/**
+ * 获取接触边缘类型
+ * @param horizontalCloseEdge
+ * @param verticalCloseEdge
+ * @param reachState
+ */
+export function getReachType({
+  horizontalCloseEdge,
+  verticalCloseEdge,
+  reachState,
+}: {
+  horizontalCloseEdge: CloseEdgeEnum;
+  verticalCloseEdge: CloseEdgeEnum;
+  reachState: ReachTypeEnum;
+}): ReachTypeEnum {
+  if (
+    (horizontalCloseEdge && reachState === ReachTypeEnum.Normal) ||
+    reachState === ReachTypeEnum.XReach
+  ) {
+    return ReachTypeEnum.XReach;
+  } else if (
+    (verticalCloseEdge && reachState === ReachTypeEnum.Normal) ||
+    reachState === ReachTypeEnum.YReach
+  ) {
+    return ReachTypeEnum.YReach;
+  }
+  return ReachTypeEnum.Normal;
+}

+ 3 - 5
src/utils/getMultipleTouchPosition.ts

@@ -3,13 +3,13 @@ import React from 'react';
 /**
  * 从 Touch 事件中获取两个触控中心位置
  */
-const getMultipleTouchPosition = (
+export default function getMultipleTouchPosition(
   evt: React.TouchEvent,
 ): {
   clientX: number;
   clientY: number;
   touchLength: number;
-} => {
+} {
   const { clientX, clientY } = evt.touches[0];
   if (evt.touches.length >= 2) {
     const { clientX: nextClientX, clientY: nextClientY } = evt.touches[1];
@@ -22,6 +22,4 @@ const getMultipleTouchPosition = (
     };
   }
   return { clientX, clientY, touchLength: 0 };
-};
-
-export default getMultipleTouchPosition;
+}

+ 7 - 7
src/utils/getPositionOnMoveOrScale.ts

@@ -1,7 +1,7 @@
 /**
  * 获取移动或缩放之后的中心点
  */
-const getPositionOnMoveOrScale = ({
+export default function getPositionOnMoveOrScale({
   x,
   y,
   clientX,
@@ -19,7 +19,7 @@ const getPositionOnMoveOrScale = ({
   x: number;
   y: number;
   scale: number;
-} => {
+} {
   const { innerWidth, innerHeight } = window;
   const centerClientX = innerWidth / 2;
   const centerClientY = innerHeight / 2;
@@ -30,13 +30,13 @@ const getPositionOnMoveOrScale = ({
   // 放大偏移量
   const offsetScale = toScale / fromScale;
   // 偏移位置
-  const originX = clientX - (clientX - lastPositionX) * offsetScale - centerClientX;
-  const originY = clientY - (clientY - lastPositionY) * offsetScale - centerClientY;
+  const originX =
+    clientX - (clientX - lastPositionX) * offsetScale - centerClientX;
+  const originY =
+    clientY - (clientY - lastPositionY) * offsetScale - centerClientY;
   return {
     x: originX,
     y: originY,
     scale: toScale,
   };
-};
-
-export default getPositionOnMoveOrScale;
+}

+ 3 - 5
src/utils/getSuitableImageSize.ts

@@ -1,13 +1,13 @@
 /**
  * 获取图片合适的大小
  */
-const getSuitableImageSize = (
+export default function getSuitableImageSize(
   naturalWidth: number,
   naturalHeight: number,
 ): {
   width: number;
   height: number;
-} => {
+} {
   let width = 0;
   let height = 0;
   const { innerWidth, innerHeight } = window;
@@ -35,6 +35,4 @@ const getSuitableImageSize = (
     width: Math.floor(width),
     height: Math.floor(height),
   };
-};
-
-export default getSuitableImageSize;
+}

+ 3 - 5
src/utils/slideToPosition.ts

@@ -5,7 +5,7 @@ import { getClosedHorizontal, getClosedVertical } from './getCloseEdge';
 /**
  * 适应到合适的图片偏移量
  */
-const slideToPosition = ({
+export default function slideToPosition({
   x,
   y,
   lastX,
@@ -26,7 +26,7 @@ const slideToPosition = ({
 }): {
   x: number;
   y: number;
-} => {
+} {
   const moveTime = Date.now() - touchedTime;
 
   // 初始速度
@@ -84,6 +84,4 @@ const slideToPosition = ({
     x: currentX,
     y: currentY,
   };
-};
-
-export default slideToPosition;
+}

+ 1 - 6
src/variables.ts

@@ -14,14 +14,9 @@ export const maxMoveOffset: number = 40;
 export const horizontalOffset: number = 20;
 
 /**
- * 最小触发边缘事件距离
- */
-export const minReachOffset: number = 10;
-
-/**
  * 最小初始响应距离
  */
-export const minStartTouchOffset: number = 100;
+export const minStartTouchOffset: number = 60;
 
 /**
  * 默认背景透明度