liumingyi_1 5 年之前
父節點
當前提交
5bbcab3f9d
共有 6 個文件被更改,包括 63 次插入133 次删除
  1. 0 4
      example/package.json
  2. 2 54
      example/yarn.lock
  3. 1 0
      src/Photo.tsx
  4. 3 5
      src/PhotoSlider.tsx
  5. 19 20
      src/PhotoView.tsx
  6. 38 50
      src/components/SlideWrap.tsx

+ 0 - 4
example/package.json

@@ -5,10 +5,6 @@
   "dependencies": {
     "@types/jest": "24.0.22",
     "@types/node": "12.12.6",
-    "@types/react": "16.9.11",
-    "@types/react-dom": "16.9.3",
-    "react": "^16.11.0",
-    "react-dom": "^16.11.0",
     "react-photo-view": "link:..",
     "react-scripts": "3.2.0",
     "styled-components": "^4.4.1",

+ 2 - 54
example/yarn.lock

@@ -1297,31 +1297,11 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.6.tgz#a47240c10d86a9a57bb0c633f0b2e0aea9ce9253"
   integrity sha512-FjsYUPzEJdGXjwKqSpE0/9QEh6kzhTAeObA54rn6j3rR4C/mzpI9L0KNfoeASSPMMdxIsoJuCLDWcM/rVjIsSA==
 
-"@types/prop-types@*":
-  version "15.7.3"
-  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
-  integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
-
 "@types/q@^1.5.1":
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
   integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
 
-"@types/react-dom@16.9.3":
-  version "16.9.3"
-  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.3.tgz#4006ff0e13958af91313869077c04cb20d9b9d04"
-  integrity sha512-FUuZKXPr9qlzUT9lhuzrZgLjH63TvNn28Ch3MvKG4B+F52zQtO8DtE0Opbncy3xaucNZM2WIPfuNTgkbKx5Brg==
-  dependencies:
-    "@types/react" "*"
-
-"@types/react@*", "@types/react@16.9.11":
-  version "16.9.11"
-  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.11.tgz#70e0b7ad79058a7842f25ccf2999807076ada120"
-  integrity sha512-UBT4GZ3PokTXSWmdgC/GeCGEJXE5ofWyibCcecRLUVN2ZBpXQGVgQGtG2foS7CrTKFKlQVVswLvf7Js6XA/CVQ==
-  dependencies:
-    "@types/prop-types" "*"
-    csstype "^2.2.0"
-
 "@types/stack-utils@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@@ -3100,11 +3080,6 @@ cssstyle@^1.0.0, cssstyle@^1.1.1:
   dependencies:
     cssom "0.3.x"
 
-csstype@^2.2.0:
-  version "2.6.7"
-  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5"
-  integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==
-
 cyclist@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@@ -6056,7 +6031,7 @@ loglevel@^1.4.1:
   resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56"
   integrity sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g==
 
-loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
+loose-envify@^1.0.0, loose-envify@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -7910,7 +7885,7 @@ prompts@^2.0.1:
     kleur "^3.0.3"
     sisteransi "^1.0.3"
 
-prop-types@^15.5.4, prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@^15.5.4, prop-types@^15.7.2:
   version "15.7.2"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
   integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -8117,16 +8092,6 @@ react-dev-utils@^9.1.0:
     strip-ansi "5.2.0"
     text-table "0.2.0"
 
-react-dom@^16.11.0:
-  version "16.11.0"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.11.0.tgz#7e7c4a5a85a569d565c2462f5d345da2dd849af5"
-  integrity sha512-nrRyIUE1e7j8PaXSPtyRKtz+2y9ubW/ghNgqKFHHAHaeP0fpF5uXR+sq8IMRHC+ZUxw7W9NyCDTBtwWxvkb0iA==
-  dependencies:
-    loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    prop-types "^15.6.2"
-    scheduler "^0.17.0"
-
 react-error-overlay@^6.0.3:
   version "6.0.3"
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.3.tgz#c378c4b0a21e88b2e159a3e62b2f531fd63bf60d"
@@ -8207,15 +8172,6 @@ react-scripts@3.2.0:
   optionalDependencies:
     fsevents "2.0.7"
 
-react@^16.11.0:
-  version "16.11.0"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.11.0.tgz#d294545fe62299ccee83363599bf904e4a07fdbb"
-  integrity sha512-M5Y8yITaLmU0ynd0r1Yvfq98Rmll6q8AxaEe88c8e7LxO8fZ2cNgmFt0aGAS9wzf1Ao32NKXtCl+/tVVtkxq6g==
-  dependencies:
-    loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    prop-types "^15.6.2"
-
 read-pkg-up@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
@@ -8660,14 +8616,6 @@ saxes@^3.1.9:
   dependencies:
     xmlchars "^2.1.1"
 
-scheduler@^0.17.0:
-  version "0.17.0"
-  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.17.0.tgz#7c9c673e4ec781fac853927916d1c426b6f3ddfe"
-  integrity sha512-7rro8Io3tnCPuY4la/NuI5F2yfESpnfZyT6TtkXnSWVkcu0BCDJ+8gk5ozUaFaxpIyNuWAPXrH0yFcSi28fnDA==
-  dependencies:
-    loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-
 schema-utils@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"

+ 1 - 0
src/Photo.tsx

@@ -94,6 +94,7 @@ export default class Photo extends React.PureComponent<
       className,
       loadingElement,
       brokenElement,
+      onPhotoResize,
       ...restProps
     } = this.props;
     const { loaded, broken, width, height } = this.state;

+ 3 - 5
src/PhotoSlider.tsx

@@ -1,5 +1,6 @@
 import React from 'react';
 import classNames from 'classnames';
+import debounce from 'lodash.debounce';
 import PhotoView from './PhotoView';
 import SlideWrap from './components/SlideWrap';
 import CloseSVG from './components/CloseSVG';
@@ -81,6 +82,7 @@ export default class PhotoSlider extends React.Component<
       photoScale: 1,
       overlayVisible: true,
     };
+    this.handleResize = debounce(this.handleResize, 32);
   }
 
   componentDidMount() {
@@ -89,11 +91,6 @@ export default class PhotoSlider extends React.Component<
       translateX: index * -(window.innerWidth + horizontalOffset),
       photoIndex: index,
     });
-    window.addEventListener('resize', this.handleResize);
-  }
-
-  componentWillUnmount() {
-    window.removeEventListener('resize', this.handleResize);
   }
 
   handleClose = () => {
@@ -336,6 +333,7 @@ export default class PhotoSlider extends React.Component<
                   }}
                   loadingElement={loadingElement}
                   brokenElement={brokenElement}
+                  onPhotoResize={this.handleResize}
                 />
               );
             })}

+ 19 - 20
src/PhotoView.tsx

@@ -12,7 +12,7 @@ import { maxScale, minReachOffset, minScale, scaleBuffer } from './variables';
 import {
   ReachFunction,
   PhotoTapFunction,
-  ReachTypeEnum,
+  ReachTypeEnum, CloseEdgeEnum,
 } from './types';
 import './PhotoView.less';
 
@@ -151,18 +151,21 @@ export default class PhotoView extends React.Component<
       } = this.state;
       let currentX = x;
       let currentY = y;
-      // 边缘状态
+      // 边缘超出状态
+      const horizontalCloseEdge = getClosedHorizontal(x, scale, width);
+      const verticalCloseEdge = getClosedVertical(y, scale, height);
+      // 边缘触发状态
       let currentReachState = ReachTypeEnum.Normal;
       if (touchLength === 0) {
-        currentX = newClientX - clientX + lastX;
-        currentY = newClientY - clientY + lastY;
+        // 非正常滑动则响应距离减半
+        currentX = (newClientX - clientX) / (horizontalCloseEdge !== CloseEdgeEnum.Normal ? 2 : 1)  + lastX;
+        currentY = (newClientY - clientY) / (verticalCloseEdge !== CloseEdgeEnum.Normal ? 2 : 1) + lastY;
         // 边缘触发检测
         currentReachState = this.handleReachCallback({
           x: currentX,
           y: currentY,
-          width,
-          height,
-          scale,
+          horizontalCloseEdge,
+          verticalCloseEdge,
           clientX: newClientX,
           clientY: newClientY,
           reachState,
@@ -387,24 +390,20 @@ export default class PhotoView extends React.Component<
   handleReachCallback = ({
     x,
     y,
-    width,
-    height,
-    scale,
     clientX,
     clientY,
+    horizontalCloseEdge,
+    verticalCloseEdge,
     reachState,
   }: {
     x: number,
     y: number,
-    width: number,
-    height: number,
-    scale: number,
     clientX: number,
     clientY: number,
+    horizontalCloseEdge: CloseEdgeEnum,
+    verticalCloseEdge: CloseEdgeEnum,
     reachState: ReachTypeEnum,
-  }): number => {
-    const horizontalType = getClosedHorizontal(x, scale, width);
-    const verticalType = getClosedVertical(y, scale, height);
+  }): ReachTypeEnum => {
     const {
       onReachTopMove,
       onReachRightMove,
@@ -414,7 +413,7 @@ export default class PhotoView extends React.Component<
     //  触碰到边缘
     if (
       onReachLeftMove
-      && (horizontalType
+      && (horizontalCloseEdge
       && x > minReachOffset
       && reachState === ReachTypeEnum.Normal
       || reachState === ReachTypeEnum.XReach)
@@ -423,7 +422,7 @@ export default class PhotoView extends React.Component<
       return ReachTypeEnum.XReach;
     } else if (
       onReachRightMove
-      && (horizontalType
+      && (horizontalCloseEdge
       && x < -minReachOffset
       && reachState === ReachTypeEnum.Normal
       || reachState === ReachTypeEnum.XReach)
@@ -432,7 +431,7 @@ export default class PhotoView extends React.Component<
       return ReachTypeEnum.XReach;
     } else if (
       onReachTopMove
-      && (verticalType
+      && (verticalCloseEdge
       && y > minReachOffset
       && reachState === ReachTypeEnum.Normal
       || reachState === ReachTypeEnum.YReach)
@@ -441,7 +440,7 @@ export default class PhotoView extends React.Component<
       return ReachTypeEnum.YReach;
     } else if (
       onReachBottomMove
-      && (verticalType
+      && (verticalCloseEdge
       && y < -minReachOffset
       && reachState === ReachTypeEnum.Normal
       || reachState === ReachTypeEnum.YReach)

+ 38 - 50
src/components/SlideWrap.tsx

@@ -3,54 +3,42 @@ import { createPortal } from 'react-dom';
 import classNames from 'classnames';
 import './SlideWrap.less';
 
-export default class SlideWrap extends React.Component<{
-  className?: string;
-  children: any;
-}> {
-  static displayName = 'SlideWrap';
-
-  private dialogNode;
-  private originalOverflow;
-
-  constructor(props) {
-    super(props);
-
+const SlideWrap: React.FC<{ className?: string }> = ({
+  className,
+  children,
+}) => {
+  const [dialogNode] = React.useState(() => {
     // 创建容器
-    this.dialogNode = document.createElement('section');
-    document.body.appendChild(this.dialogNode);
-  }
-
-  componentDidMount() {
-    this.preventScroll();
-  }
-
-  componentWillUnmount() {
-    this.allowScroll();
-    // 清除容器
-    document.body.removeChild(this.dialogNode);
-    this.dialogNode = undefined;
-  }
-
-  preventScroll = () => {
-    const { style } = document.body;
-    this.originalOverflow = style.overflow;
-    style.overflow = 'hidden';
-  };
-
-  allowScroll = () => {
-    const { style } = document.body;
-    style.overflow = this.originalOverflow;
-    this.originalOverflow = undefined;
-  };
-
-  render() {
-    const { className, children } = this.props;
-
-    return createPortal(
-      <div className={classNames('PhotoView__SlideWrap', className)}>
-        {children}
-      </div>,
-      this.dialogNode,
-    );
-  }
-}
+    const dialogNode = document.createElement('section');
+    document.body.appendChild(dialogNode);
+    return dialogNode;
+  });
+  const originalOverflowCallback = React.useRef('');
+
+  React.useEffect(
+    () => {
+      const { style } = document.body;
+      originalOverflowCallback.current = style.overflow;
+      style.overflow = 'hidden';
+
+      return () => {
+        const { style } = document.body;
+        style.overflow = originalOverflowCallback.current;
+        // 清除容器
+        document.body.removeChild(dialogNode);
+      };
+    },
+    [] as readonly [],
+  );
+
+  return createPortal(
+    <div className={classNames('PhotoView__SlideWrap', className)}>
+      {children}
+    </div>,
+    dialogNode,
+  );
+};
+
+SlideWrap.displayName = 'SlideWrap';
+
+export default SlideWrap;