Browse Source

feat: 支持自定义工具栏、旋转 API

MinJieLiu 5 years ago
parent
commit
4c063151f9

+ 4 - 2
README.md

@@ -10,14 +10,15 @@ Demo: [https://minjieliu.github.io/react-photo-view](https://minjieliu.github.io
 
 ### 特性
 
-1.  支持左右切换导航、上/下滑关闭、双击放大/缩小、双指放大/缩小/平移、键盘导航/关闭、点击切换控件等
+1.  支持左右切换导航、上/下滑关闭、双击放大/缩小、双指放大/缩小/平移、键盘导航/关闭、旋转、点击切换控件等
 1.  打开/关闭缩放动画
 1.  自适应图像适应
+1.  长图模式
 1.  支持桌面端(兼容 IE10+)/移动端
 1.  轻量的体积
 1.  高度的扩展性
 1.  支持服务端渲染
-1.  基于 `typescript` 友好的语法提示
+1.  基于 `typescript`
 
 ## 开始使用
 
@@ -76,6 +77,7 @@ function ImageView() {
 | bannerVisible  | boolean                           | 否   | 导航条 visible,默认 true  |
 | introVisible   | boolean                           | 否   | 简介 visible,默认 true    |
 | overlayRender  | (overlayProps) => React.ReactNode | 否   | 自定义渲染                 |
+| toolbarRender  | (overlayProps) => React.ReactNode | 否   | 工具栏渲染                 |
 | className      | string                            | 否   | className                  |
 | maskClassName  | string                            | 否   | 遮罩 className             |
 | viewClassName  | string                            | 否   | 图片容器 className         |

+ 68 - 1
example/src/index.tsx

@@ -1,6 +1,6 @@
 import * as React from 'react';
 import styled from 'styled-components';
-import { PhotoSlider } from 'react-photo-view';
+import { PhotoProvider, PhotoSlider, PhotoConsumer } from 'react-photo-view';
 import { IPhotoProvider } from 'react-photo-view/dist/PhotoProvider';
 import { IPhotoConsumer } from 'react-photo-view/dist/PhotoConsumer';
 import { IPhotoSliderProps } from 'react-photo-view/dist/PhotoSlider';
@@ -83,6 +83,73 @@ export const ControlledView = () => {
   );
 };
 
+const FullScreenIcon = (props: React.HTMLAttributes<any>) => {
+  const [fullscreen, setFullscreen] = React.useState<boolean>(false);
+  React.useEffect(() => {
+    document.onfullscreenchange = () => {
+      setFullscreen(Boolean(document.fullscreenElement));
+    };
+  }, []);
+  return (
+    <svg
+      className="PhotoView-PhotoSlider__toolbarIcon"
+      fill="white"
+      width="44"
+      height="44"
+      viewBox="0 0 768 768"
+      {...props}
+    >
+      {fullscreen ? (
+        <path d="M511.5 256.5h96v63h-159v-159h63v96zM448.5 607.5v-159h159v63h-96v96h-63zM256.5 256.5v-96h63v159h-159v-63h96zM160.5 511.5v-63h159v159h-63v-96h-96z" />
+      ) : (
+        <path d="M448.5 160.5h159v159h-63v-96h-96v-63zM544.5 544.5v-96h63v159h-159v-63h96zM160.5 319.5v-159h159v63h-96v96h-63zM223.5 448.5v96h96v63h-159v-159h63z" />
+      )}
+    </svg>
+  );
+};
+
+export const WithToolbar = () => {
+  function toggleFullScreen() {
+    if (document.fullscreenElement) {
+      document.exitFullscreen();
+    } else {
+      const element = document.getElementById('PhotoView_Slider');
+      if (element) {
+        element.requestFullscreen();
+      }
+    }
+  }
+  return (
+    <PhotoProvider
+      toolbarRender={({ rotate, onRotate }) => {
+        return (
+          <>
+            <svg
+              className="PhotoView-PhotoSlider__toolbarIcon"
+              onClick={() => onRotate(rotate + 90)}
+              width="44"
+              height="44"
+              fill="white"
+              viewBox="0 0 768 768"
+            >
+              <path d="M565.5 202.5l75-75v225h-225l103.5-103.5c-34.5-34.5-82.5-57-135-57-106.5 0-192 85.5-192 192s85.5 192 192 192c84 0 156-52.5 181.5-127.5h66c-28.5 111-127.5 192-247.5 192-141 0-255-115.5-255-256.5s114-256.5 255-256.5c70.5 0 135 28.5 181.5 75z" />
+            </svg>
+            <FullScreenIcon onClick={toggleFullScreen} />
+          </>
+        );
+      }}
+    >
+      <ImageList>
+        {photoImages.map((item, index) => (
+          <PhotoConsumer key={index} src={item} intro={item}>
+            <ViewBox viewImage={item} />
+          </PhotoConsumer>
+        ))}
+      </ImageList>
+    </PhotoProvider>
+  );
+};
+
 export function IPhotoProviderForwardProps(props: IPhotoProvider) {}
 
 export function IPhotoConsumerForwardProps(props: IPhotoConsumer) {}

+ 9 - 1
example/src/stories/Test.stories.mdx

@@ -9,7 +9,7 @@ import {
   Button,
   DefaultImage,
   ControlledView,
-  
+  WithToolbar,
   IPhotoProviderForwardProps,
   IPhotoConsumerForwardProps,
   IPhotoSliderForwardProps,
@@ -91,6 +91,14 @@ import defaultPhoto from '../default-photo.svg';
   </Story>
 </Preview>
 
+## 自定义工具栏
+
+<Preview>
+  <Story name="自定义工具栏">
+    <WithToolbar />
+  </Story>
+</Preview>
+
 # Props
 
 ### PhotoProvider

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "react-photo-view",
-  "version": "0.4.5",
+  "version": "0.5.0",
   "description": "一款精致的 React 的图片预览组件",
   "author": "MinJieLiu",
   "license": "MIT",

+ 3 - 1
src/Photo.tsx

@@ -11,6 +11,7 @@ export interface IPhotoProps extends React.HTMLAttributes<any> {
   broken: boolean;
   width: number;
   height: number;
+  rotate: number;
   className?: string;
   onImageLoad: (PhotoParams, callback?: Function) => void;
   loadingElement?: JSX.Element;
@@ -23,6 +24,7 @@ const Photo: React.FC<IPhotoProps> = ({
   broken,
   width,
   height,
+  rotate,
   className,
   onImageLoad,
   loadingElement,
@@ -38,7 +40,7 @@ const Photo: React.FC<IPhotoProps> = ({
         loaded: true,
         naturalWidth,
         naturalHeight,
-        ...getSuitableImageSize(naturalWidth, naturalHeight),
+        ...getSuitableImageSize(naturalWidth, naturalHeight, rotate),
       });
     }
   }

+ 3 - 1
src/PhotoSlider.less

@@ -69,10 +69,12 @@
   }
 
   &-PhotoSlider__BannerRight {
+    display: flex;
+    align-items: center;
     height: 100%;
   }
 
-  &-PhotoSlider__Close {
+  &-PhotoSlider__toolbarIcon {
     box-sizing: border-box;
     padding: 10px;
     opacity: 0.75;

+ 31 - 12
src/PhotoSlider.tsx

@@ -7,7 +7,7 @@ import Close from './components/Close';
 import ArrowLeft from './components/ArrowLeft';
 import ArrowRight from './components/ArrowRight';
 import isTouchDevice from './utils/isTouchDevice';
-import { dataType, IPhotoProviderBase, ReachTypeEnum, ShowAnimateEnum } from './types';
+import { dataType, IPhotoProviderBase, overlayRenderProps, ReachTypeEnum, ShowAnimateEnum } from './types';
 import { defaultOpacity, horizontalOffset, maxMoveOffset } from './variables';
 import './PhotoSlider.less';
 
@@ -46,6 +46,8 @@ type PhotoSliderState = {
   overlayVisible: boolean;
   // 可下拉关闭
   canPullClose: boolean;
+  // 旋转集合
+  rotatingMap: Map<number, number>;
 };
 
 export default class PhotoSlider extends React.Component<IPhotoSliderProps, PhotoSliderState> {
@@ -83,6 +85,8 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
       lastBackdropOpacity: defaultOpacity,
       overlayVisible: true,
       canPullClose: true,
+
+      rotatingMap: new Map<number, number>(),
     };
   }
 
@@ -140,6 +144,14 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
     });
   };
 
+  handleRotate = (rotating: number) => {
+    const { photoIndex, rotatingMap } = this.state;
+    rotatingMap.set(photoIndex, rotating);
+    this.setState({
+      rotatingMap,
+    });
+  };
+
   handleKeyDown = (evt: KeyboardEvent) => {
     const { visible } = this.props;
     if (visible) {
@@ -298,6 +310,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
       bannerVisible,
       introVisible,
       overlayRender,
+      toolbarRender,
       loadingElement,
       brokenElement,
     } = this.props;
@@ -308,6 +321,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
       backdropOpacity,
       lastBackdropOpacity,
       overlayVisible,
+      rotatingMap,
       shouldTransition,
     } = this.state;
     const imageLength = images.length;
@@ -324,7 +338,17 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
             const currentOverlayVisible = overlayVisible && showAnimateType === ShowAnimateEnum.None;
             // 关闭过程中使用下拉保存的透明度
             const currentOpacity = visible ? backdropOpacity : lastBackdropOpacity;
-
+            // 覆盖物参数
+            const overlayParams: overlayRenderProps = {
+              images,
+              index: photoIndex,
+              visible,
+              onClose: this.handleClose,
+              onIndexChange: this.handleIndexChange,
+              overlayVisible: currentOverlayVisible,
+              onRotate: this.handleRotate,
+              rotate: rotatingMap.get(photoIndex) || 0,
+            };
             return (
               <SlideWrap
                 className={classNames(
@@ -335,6 +359,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
                   className,
                 )}
                 role="dialog"
+                id="PhotoView_Slider"
                 onClick={e => e.stopPropagation()}
               >
                 <div
@@ -353,7 +378,8 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
                       {photoIndex + 1} / {imageLength}
                     </div>
                     <div className="PhotoView-PhotoSlider__BannerRight">
-                      <Close className="PhotoView-PhotoSlider__Close" onClick={this.handleClose} />
+                      {toolbarRender && toolbarRender(overlayParams)}
+                      <Close className="PhotoView-PhotoSlider__toolbarIcon" onClick={this.handleClose} />
                     </div>
                   </div>
                 )}
@@ -391,6 +417,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
                         isActive={photoIndex === realIndex}
                         showAnimateType={showAnimateType}
                         originRect={originRect}
+                        rotate={rotatingMap.get(realIndex) || 0}
                       />
                     );
                   })}
@@ -411,15 +438,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
                 {Boolean(introVisible && overlayIntro) && (
                   <div className="PhotoView-PhotoSlider__FooterWrap">{overlayIntro}</div>
                 )}
-                {overlayRender &&
-                  overlayRender({
-                    images,
-                    index: photoIndex,
-                    visible,
-                    onClose: this.handleClose,
-                    onIndexChange: this.handleIndexChange,
-                    overlayVisible: currentOverlayVisible,
-                  })}
+                {overlayRender && overlayRender(overlayParams)}
               </SlideWrap>
             );
           }

+ 5 - 0
src/PhotoView.less

@@ -45,6 +45,11 @@
   }
 
   &__PhotoBox {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 0;
+    height: 0;
   }
 
   &__PhotoMask {

+ 31 - 18
src/PhotoView.tsx

@@ -6,7 +6,7 @@ import isTouchDevice from './utils/isTouchDevice';
 import getMultipleTouchPosition from './utils/getMultipleTouchPosition';
 import getPositionOnMoveOrScale from './utils/getPositionOnMoveOrScale';
 import slideToPosition from './utils/slideToPosition';
-import { getReachType, getClosedHorizontal, getClosedVertical } from './utils/getCloseEdge';
+import { getReachType, getClosedEdge } from './utils/getCloseEdge';
 import withContinuousTap, { TapFuncType } from './utils/withContinuousTap';
 import getAnimateOrigin from './utils/getAnimateOrigin';
 import { maxScale, minStartTouchOffset, minScale, scaleBuffer } from './variables';
@@ -36,6 +36,8 @@ export interface IPhotoViewProps {
   loadingElement?: JSX.Element;
   // 加载失败 Element
   brokenElement?: JSX.Element;
+  // 旋转状态
+  rotate: number;
 
   // Photo 点击事件
   onPhotoTap: PhotoTapFunction;
@@ -132,14 +134,19 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
     window.addEventListener('resize', this.handleResize);
   }
 
-  componentWillUnmount() {
-    if (isTouchDevice) {
-      window.removeEventListener('touchmove', this.handleTouchMove);
-      window.removeEventListener('touchend', this.handleTouchEnd);
-    } else {
-      window.removeEventListener('mousemove', this.handleMouseMove);
-      window.removeEventListener('mouseup', this.handleMouseUp);
+  componentDidUpdate(prevProps: Readonly<IPhotoViewProps>) {
+    const { rotate } = this.props;
+    if (rotate !== prevProps.rotate) {
+      const { naturalWidth, naturalHeight } = this.state;
+      this.setState(getSuitableImageSize(naturalWidth, naturalHeight, rotate));
     }
+  }
+
+  componentWillUnmount() {
+    window.removeEventListener('touchmove', this.handleTouchMove);
+    window.removeEventListener('touchend', this.handleTouchEnd);
+    window.removeEventListener('mousemove', this.handleMouseMove);
+    window.removeEventListener('mouseup', this.handleMouseUp);
     window.removeEventListener('resize', this.handleResize);
   }
 
@@ -148,10 +155,10 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
   };
 
   handleResize = () => {
-    const { onPhotoResize } = this.props;
+    const { onPhotoResize, rotate } = this.props;
     const { loaded, naturalWidth, naturalHeight } = this.state;
     if (loaded) {
-      this.setState(getSuitableImageSize(naturalWidth, naturalHeight));
+      this.setState(getSuitableImageSize(naturalWidth, naturalHeight, rotate));
       if (onPhotoResize) {
         onPhotoResize();
       }
@@ -173,10 +180,8 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
   };
 
   onMove = (newClientX: number, newClientY: number, touchLength: number = 0) => {
-    const { onReachMove, isActive } = this.props;
+    const { onReachMove, isActive, rotate } = this.props;
     const {
-      width,
-      height,
       naturalWidth,
       x,
       y,
@@ -193,6 +198,11 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
       maskTouched,
     } = this.state;
     if ((touched || maskTouched) && isActive) {
+      let { width, height } = this.state;
+      // 若图片不是水平则调换属性
+      if (rotate % 180 !== 0) {
+        [width, height] = [height, width];
+      }
       // 单指最小缩放下,以初始移动距离来判断意图
       if (touchLength === 0 && this.initialTouchState === TouchStartEnum.Normal) {
         const isStillX = Math.abs(newClientX - clientX) <= minStartTouchOffset;
@@ -220,8 +230,8 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
       let currentReachState = ReachTypeEnum.Normal;
       if (touchLength === 0) {
         // 边缘超出状态
-        const horizontalCloseEdge = getClosedHorizontal(offsetX + lastX, scale, width);
-        const verticalCloseEdge = getClosedVertical(offsetY + lastY, scale, height);
+        const horizontalCloseEdge = getClosedEdge(offsetX + lastX, scale, width, window.innerWidth);
+        const verticalCloseEdge = getClosedEdge(offsetY + lastY, scale, height, window.innerHeight);
         // 边缘触发检测
         currentReachState = getReachType({
           initialTouchState: this.initialTouchState,
@@ -363,7 +373,7 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
   handleUp = (newClientX: number, newClientY: number) => {
     // 重置响应状态
     this.initialTouchState = TouchStartEnum.Normal;
-    const { onReachUp, onPhotoTap, onMaskTap, isActive } = this.props;
+    const { onReachUp, onPhotoTap, onMaskTap, isActive, rotate } = this.props;
     const {
       width,
       height,
@@ -397,6 +407,7 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
                 width,
                 height,
                 scale,
+                rotate,
                 touchedTime,
               })
             : {
@@ -437,6 +448,7 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
       viewClassName,
       className,
       style,
+      rotate,
       loadingElement,
       brokenElement,
       isActive,
@@ -445,7 +457,7 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
     } = this.props;
     const { width, height, loaded, x, y, scale, touched, broken } = this.state;
 
-    const transform = `translate3d(${x}px, ${y}px, 0) scale(${scale})`;
+    const transform = `translate3d(${x}px, ${y}px, 0) scale(${scale}) rotate(${rotate}deg)`;
 
     return (
       <div className={classNames('PhotoView__PhotoWrap', viewClassName)} style={style}>
@@ -460,7 +472,7 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
             PhotoView__animateOut: loaded && showAnimateType === ShowAnimateEnum.Out,
           })}
           style={{
-            transformOrigin: loaded ? getAnimateOrigin(originRect, width, height) : undefined,
+            transformOrigin: loaded ? getAnimateOrigin(originRect, 0, 0) : undefined,
           }}
         >
           <Photo
@@ -470,6 +482,7 @@ export default class PhotoView extends React.Component<IPhotoViewProps, typeof i
             height={height}
             loaded={loaded}
             broken={broken}
+            rotate={rotate}
             onMouseDown={isTouchDevice ? undefined : this.handleMouseDown}
             onTouchStart={isTouchDevice ? this.handleTouchStart : undefined}
             onWheel={this.handleWheel}

+ 7 - 1
src/types.ts

@@ -14,7 +14,7 @@ export type dataType = {
   intro?: React.ReactNode;
 };
 
-type overlayRenderProps = {
+export type overlayRenderProps = {
   // 图片列表
   images: dataType[];
   // 图片当前索引
@@ -27,6 +27,10 @@ type overlayRenderProps = {
   onIndexChange: (photoIndex: number) => void;
   // 覆盖物可见度
   overlayVisible: boolean;
+  // 当前图片旋转角度
+  rotate: number;
+  // 旋转事件
+  onRotate: (rotate: number) => void;
 };
 
 export interface IPhotoProviderBase {
@@ -40,6 +44,8 @@ export interface IPhotoProviderBase {
   introVisible?: boolean;
   // 自定义渲染
   overlayRender?: (overlayProps: overlayRenderProps) => React.ReactNode;
+  // 工具栏渲染
+  toolbarRender?: (overlayProps: overlayRenderProps) => React.ReactNode;
   // className
   className?: string;
   // 遮罩 className

+ 10 - 33
src/utils/getCloseEdge.ts

@@ -1,45 +1,22 @@
 import { CloseEdgeEnum, ReachTypeEnum, TouchStartEnum } from '../types';
 
 /**
- * 接触左边或右边边缘
- * @param x
+ * 接触左边/上边  边/下边边缘
+ * @param position - x/y
  * @param scale
- * @param width
+ * @param size - width/height
+ * @param innerSize - innerWidth/innerHeight
  * @return CloseEdgeEnum
  */
-export function getClosedHorizontal(x: number, scale: number, width: number): CloseEdgeEnum {
-  const { innerWidth } = window;
-  const currentWidth = width * scale;
+export function getClosedEdge(position: number, scale: number, size: number, innerSize: number): CloseEdgeEnum {
+  const currentWidth = size * scale;
   // 图片超出的宽度
-  const outOffsetX = (currentWidth - innerWidth) / 2;
-  if (currentWidth <= innerWidth) {
+  const outOffsetX = (currentWidth - innerSize) / 2;
+  if (currentWidth <= innerSize) {
     return CloseEdgeEnum.Small;
-  } else if (x > 0 && outOffsetX - x <= 0) {
+  } else if (position > 0 && outOffsetX - position <= 0) {
     return CloseEdgeEnum.Before;
-  } else if (x < 0 && outOffsetX + x <= 0) {
-    return CloseEdgeEnum.After;
-  }
-  return CloseEdgeEnum.Normal;
-}
-
-/**
- * 接触上边或下边边缘
- * @param y
- * @param scale
- * @param height
- * @return CloseEdgeEnum
- */
-export function getClosedVertical(y: number, scale: number, height: number): CloseEdgeEnum {
-  const { innerHeight } = window;
-  const currentHeight = height * scale;
-  // 图片超出的高度
-  const outOffsetY = (currentHeight - innerHeight) / 2;
-
-  if (currentHeight <= innerHeight) {
-    return CloseEdgeEnum.Small;
-  } else if (y > 0 && outOffsetY - y <= 0) {
-    return CloseEdgeEnum.Before;
-  } else if (y < 0 && outOffsetY + y <= 0) {
+  } else if (position < 0 && outOffsetX + position <= 0) {
     return CloseEdgeEnum.After;
   }
   return CloseEdgeEnum.Normal;

+ 10 - 2
src/utils/getSuitableImageSize.ts

@@ -4,6 +4,7 @@
 export default function getSuitableImageSize(
   naturalWidth: number,
   naturalHeight: number,
+  rotate: number,
 ): {
   width: number;
   height: number;
@@ -14,7 +15,14 @@ export default function getSuitableImageSize(
   let width;
   let height;
   let y = 0;
-  const { innerWidth, innerHeight } = window;
+  let { innerWidth, innerHeight } = window;
+  const isVertical = rotate % 180 !== 0;
+
+  // 若图片不是水平则调换宽高
+  if (isVertical) {
+    [innerHeight, innerWidth] = [innerWidth, innerHeight];
+  }
+
   const autoWidth = (naturalWidth / naturalHeight) * innerHeight;
   const autoHeight = (naturalHeight / naturalWidth) * innerWidth;
 
@@ -32,7 +40,7 @@ export default function getSuitableImageSize(
     height = autoHeight;
   }
   // 长图模式
-  else if (naturalHeight / naturalWidth >= 3) {
+  else if (naturalHeight / naturalWidth >= 3 && !isVertical) {
     width = innerWidth;
     height = autoHeight;
     // 默认定位到顶部区域

+ 14 - 6
src/utils/slideToPosition.ts

@@ -1,6 +1,6 @@
 import { maxTouchTime, slideAcceleration } from '../variables';
 import { CloseEdgeEnum } from '../types';
-import { getClosedHorizontal, getClosedVertical } from './getCloseEdge';
+import { getClosedEdge } from './getCloseEdge';
 
 /**
  * 适应到合适的图片偏移量
@@ -13,6 +13,7 @@ export default function slideToPosition({
   width,
   height,
   scale,
+  rotate,
   touchedTime,
 }: {
   x: number;
@@ -22,6 +23,7 @@ export default function slideToPosition({
   width: number;
   height: number;
   scale: number;
+  rotate: number;
   touchedTime: number;
 }): {
   x: number;
@@ -38,8 +40,13 @@ export default function slideToPosition({
   const slideTimeY = Math.abs(speedY / slideAcceleration);
 
   // 计划滑动位置
-  const planX = Math.floor(x + speedX * slideTimeX);
-  const planY = Math.floor(y + speedY * slideTimeY);
+  let planX = Math.floor(x + speedX * slideTimeX);
+  let planY = Math.floor(y + speedY * slideTimeY);
+
+  // 若图片不是水平则调换属性
+  if (rotate % 180 !== 0) {
+    [width, height] = [height, width];
+  }
 
   let currentX = planX;
   let currentY = planY;
@@ -49,8 +56,8 @@ export default function slideToPosition({
   const outOffsetX = (width * scale - innerWidth) / 2;
   const outOffsetY = (height * scale - innerHeight) / 2;
 
-  const horizontalCloseEdge = getClosedHorizontal(planX, scale, width);
-  const verticalCloseEdge = getClosedVertical(planY, scale, height);
+  const horizontalCloseEdge = getClosedEdge(planX, scale, width, innerWidth);
+  const verticalCloseEdge = getClosedEdge(planY, scale, height, innerHeight);
 
   // x
   if (horizontalCloseEdge === CloseEdgeEnum.Small) {
@@ -72,7 +79,8 @@ export default function slideToPosition({
   // 时间过长
   if (
     moveTime >= maxTouchTime &&
-    horizontalCloseEdge === CloseEdgeEnum.Normal && verticalCloseEdge === CloseEdgeEnum.Normal
+    horizontalCloseEdge === CloseEdgeEnum.Normal &&
+    verticalCloseEdge === CloseEdgeEnum.Normal
   ) {
     return {
       x,