MinJieLiu 5 tahun lalu
induk
melakukan
3dd8ccf076

+ 24 - 9
example/stories/Index.stories.js

@@ -8,6 +8,7 @@ import image2 from './static/2.jpg';
 import image3 from './static/3.jpg';
 import image4 from './static/4.jpg';
 import image5 from './static/5.jpg';
+import defaultPhoto from './static/default-photo.svg';
 
 const photoImages = [image1, image2, image3, image4, image5];
 
@@ -31,6 +32,15 @@ const Button = styled.button`
   border: 1px solid #ccc;
   border-radius: 2px;
   cursor: pointer;
+
+  &:not(:last-child) {
+    margin-right: 12px;
+  }
+`;
+
+const DefaultImage = styled.img`
+  width: 100px;
+  height: 100px;
 `;
 
 storiesOf('基本操作', module)
@@ -56,15 +66,6 @@ storiesOf('基本操作', module)
       </ImageList>
     </PhotoProvider>
   ))
-  .add('错误图片地址', () => (
-    <PhotoProvider>
-      <ImageList>
-        <PhotoConsumer src={null}>
-          <SmallImage src={image1} />
-        </PhotoConsumer>
-      </ImageList>
-    </PhotoProvider>
-  ))
   .add('按钮触发', () => (
     <PhotoProvider>
       <ImageList>
@@ -73,4 +74,18 @@ storiesOf('基本操作', module)
         </PhotoConsumer>
       </ImageList>
     </PhotoProvider>
+  ))
+  .add('错误图片地址', () => (
+    <ImageList>
+      <PhotoProvider>
+        <PhotoConsumer src={null}>
+          <Button>无默认图</Button>
+        </PhotoConsumer>
+      </PhotoProvider>
+      <PhotoProvider brokenElement={<DefaultImage src={defaultPhoto} />}>
+        <PhotoConsumer src={null}>
+          <Button>自定义默认图</Button>
+        </PhotoConsumer>
+      </PhotoProvider>
+    </ImageList>
   ));

+ 6 - 0
example/stories/static/default-photo.svg

@@ -0,0 +1,6 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
+<path fill="#a5f4ff" d="M496.327 449.306h-480.654c-8.655 0-15.673-7.017-15.673-15.673v-355.266c0-8.656 7.018-15.673 15.673-15.673h480.653c8.656 0 15.673 7.018 15.673 15.673v355.265c0.001 8.657-7.017 15.674-15.672 15.674z"></path>
+<path fill="#ffea8c" d="M256 231.441c-28.808 0-52.245-23.437-52.245-52.245s23.437-52.245 52.245-52.245 52.245 23.437 52.245 52.245-23.437 52.245-52.245 52.245z"></path>
+<path fill="#6ddbc9" d="M512 243.835v189.797c0 8.656-7.018 15.673-15.673 15.673h-337.29c54.408-140.359 198.976-228.077 352.963-205.47z"></path>
+<path fill="#69f4db" d="M0 243.835v189.797c0 8.656 7.018 15.673 15.673 15.673h337.29c-54.41-140.359-198.976-228.077-352.963-205.47z"></path>
+</svg>

+ 2 - 0
src/PhotoSlider.tsx

@@ -196,6 +196,7 @@ export default class PhotoSlider extends React.Component<
           touched: true,
           lastClientX: clientX,
           translateX,
+          shouldTransition: true,
         };
       }
       const originOffsetClientX = clientX - lastClientX;
@@ -213,6 +214,7 @@ export default class PhotoSlider extends React.Component<
         lastClientX: lastClientX,
         translateX:
           -(innerWidth + horizontalOffset) * photoIndex + offsetClientX,
+        shouldTransition: true,
       };
     });
   };

+ 30 - 28
src/PhotoView.tsx

@@ -94,6 +94,10 @@ const initialState = {
   lastX: 0,
   // 触摸开始时图片 y 偏移量
   lastY: 0,
+  // 上一个触摸状态 x 原始坐标
+  lastMoveClientX: 0,
+  // 上一个触摸状态 y 原始坐标
+  lastMoveClientY: 0,
 
   // 触摸开始时时间
   touchedTime: 0,
@@ -169,6 +173,8 @@ export default class PhotoView extends React.Component<
       touched: true,
       clientX,
       clientY,
+      lastMoveClientX: clientX,
+      lastMoveClientY: clientY,
       lastX: prevState.x,
       lastY: prevState.y,
       lastTouchLength: touchLength,
@@ -189,6 +195,8 @@ export default class PhotoView extends React.Component<
       y,
       clientX,
       clientY,
+      lastMoveClientX,
+      lastMoveClientY,
       lastX,
       lastY,
       scale,
@@ -199,43 +207,41 @@ export default class PhotoView extends React.Component<
     } = this.state;
     if ((touched || maskTouched) && isActive) {
       // 单指最小缩放下,以初始移动距离来判断意图
-      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)) {
+      if (touchLength === 0 && this.initialTouchState === TouchStartEnum.Normal) {
+        const isStillX = Math.abs(newClientX - clientX) <= minStartTouchOffset;
+        const isStillY = Math.abs(newClientY - clientY) <= minStartTouchOffset;
+        // 初始移动距离不足
+        if (isStillX && isStillY) {
+          // Y 方向记录上次移动距离,以便平滑过渡
+          if (isStillY) {
+            this.setState({
+              lastMoveClientY: newClientY,
+            });
+          }
           return;
         }
         // 设置响应状态
-        this.initialTouchState = isBeyondX
+        this.initialTouchState = !isStillX
           ? TouchStartEnum.X
           : newClientY > clientY
             ? TouchStartEnum.YPull
             : TouchStartEnum.YPush;
       }
 
-      let currentX = x;
-      let currentY = y;
+      let offsetX = newClientX - lastMoveClientX;
+      let offsetY = newClientY - lastMoveClientY;
       // 边缘触发状态
       let currentReachState = ReachTypeEnum.Normal;
       if (touchLength === 0) {
-        currentX = newClientX - clientX + lastX;
-        const planY = newClientY - clientY + lastY;
-        const touchYOffset = this.initialTouchState === TouchStartEnum.YPush
-          ? minStartTouchOffset
-          : -minStartTouchOffset;
         // 边缘超出状态
         const { horizontalCloseEdge, verticalCloseEdge } = getCloseEdgeResult({
           initialTouchState: this.initialTouchState,
-          planX: currentX,
-          planY,
+          planX: offsetX + lastX,
+          planY: offsetY + lastY,
           scale,
           width,
           height,
         });
-        // Y 方向在初始响应状态下需要补一个距离
-        currentY = planY +
-          (this.initialTouchState === TouchStartEnum.Normal ? 0 : touchYOffset);
         // 边缘触发检测
         currentReachState = getReachType({ horizontalCloseEdge, verticalCloseEdge, reachState });
 
@@ -244,8 +250,6 @@ export default class PhotoView extends React.Component<
           onReachMove(currentReachState, newClientX, newClientY, scale);
         }
       }
-      currentX = newClientX - clientX + lastX;
-      currentY = newClientY - clientY + lastY;
       // 横向边缘触发、背景触发禁用当前滑动
       if (currentReachState === ReachTypeEnum.XReach || maskTouched) {
         this.setState({
@@ -266,14 +270,12 @@ export default class PhotoView extends React.Component<
           lastTouchLength: touchLength,
           reachState: currentReachState,
           ...getPositionOnMoveOrScale({
-            x: currentX,
-            y: currentY,
-            lastX: x,
-            lastY: y,
+            x,
+            y,
             clientX: newClientX,
             clientY: newClientY,
-            lastClientX: clientX,
-            lastClientY: clientY,
+            offsetX,
+            offsetY,
             fromScale: scale,
             toScale,
           }),
@@ -480,8 +482,8 @@ export default class PhotoView extends React.Component<
         />
         <div
           className={classNames({
-            PhotoView__animateIn: (loaded || broken) && showAnimateType === ShowAnimateEnum.In,
-            PhotoView__animateOut: (loaded || broken) && showAnimateType === ShowAnimateEnum.Out,
+            PhotoView__animateIn: loaded && showAnimateType === ShowAnimateEnum.In,
+            PhotoView__animateOut: loaded && showAnimateType === ShowAnimateEnum.Out,
           })}
           style={{
             transformOrigin: loaded ? getAnimateOrigin(originRect, width, height) : undefined,

+ 12 - 16
src/utils/getPositionOnMoveOrScale.ts

@@ -4,38 +4,34 @@
 export default function getPositionOnMoveOrScale({
   x,
   y,
-  lastX = x,
-  lastY = y,
   clientX,
   clientY,
-  lastClientX = clientX,
-  lastClientY = clientY,
+  offsetX = 0,
+  offsetY = 0,
   fromScale,
   toScale,
 }: {
   x: number;
   y: number;
-  lastX?: number;
-  lastY?: number;
   clientX: number;
   clientY: number;
-  lastClientX?: number;
-  lastClientY?: number;
+  offsetX?: number;
+  offsetY?: number;
   fromScale: number;
   toScale: number;
 }): {
   x: number;
   y: number;
   scale: number;
-  clientX: number;
-  clientY: number;
+  lastMoveClientX: number;
+  lastMoveClientY: number;
 } {
   const { innerWidth, innerHeight } = window;
   const centerClientX = innerWidth / 2;
   const centerClientY = innerHeight / 2;
   // 坐标偏移
-  const lastPositionX = centerClientX + lastX;
-  const lastPositionY = centerClientY + lastY;
+  const lastPositionX = centerClientX + x;
+  const lastPositionY = centerClientY + y;
 
   // 放大偏移量
   const offsetScale = toScale / fromScale;
@@ -45,10 +41,10 @@ export default function getPositionOnMoveOrScale({
   const originY =
     clientY - (clientY - lastPositionY) * offsetScale - centerClientY;
   return {
-    x: originX + (clientX - lastClientX),
-    y: originY + (clientY - lastClientY),
+    x: originX + offsetX,
+    y: originY + offsetY,
     scale: toScale,
-    clientX,
-    clientY,
+    lastMoveClientX: clientX,
+    lastMoveClientY: clientY,
   };
 }