MinJieLiu пре 7 година
родитељ
комит
8e912790c6



+ 1 - 1
examples/simple.tsx

@@ -30,7 +30,7 @@ const SmallImage = styled.img`
 
 class Example extends React.Component {
   state = {
-    photoImages: ['1.png', '2.jpg', '3.jpg', '4.jpg', '5.jpg', '6.jpg'],
+    photoImages: ['1.png', '2.jpg', '3.jpg', '4.jpg', '5.jpg'],
   };
 
   render() {

+ 6 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "react-photo-view",
-  "version": "0.1.6",
+  "version": "0.1.7",
   "description": "React photo preview.",
   "main": "./lib/index",
   "module": "./es/index",
@@ -10,6 +10,11 @@
     "react-photo-view",
     "photo"
   ],
+  "homepage": "https://github.com/MinJieLiu/react-photo-view",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/MinJieLiu/react-photo-view.git"
+  },
   "scripts": {
     "watch-tsc": "rc-tools run watch-tsc",
     "dist": "rc-tools run dist",

+ 21 - 6
src/PhotoSlider.tsx

@@ -2,8 +2,9 @@ import React from 'react';
 import PhotoView from './PhotoView';
 import SlideWrap from './components/SlideWrap';
 import Backdrop from './components/Backdrop';
-import { dataType } from './types';
-import { maxMoveOffset, defaultOpacity, horizontalOffset } from './variables';
+import { Close, Counter, TopBar } from './components/TopWrap';
+import { dataType, TouchTypeEnum } from './types';
+import { defaultOpacity, horizontalOffset, maxMoveOffset } from './variables';
 
 interface IPhotoSliderProps {
   // 图片列表
@@ -13,9 +14,11 @@ interface IPhotoSliderProps {
   // 可见
   visible: boolean;
   // 关闭事件
-  onClose: Function;
+  onClose: (evt?: React.MouseEvent) => void;
   // 索引改变回调
   onIndexChange?: Function;
+  // 背景可点击关闭,默认 true
+  maskClosable?: boolean;
   // 自定义容器
   overlay?: React.ReactNode;
   // className
@@ -150,9 +153,9 @@ export default class PhotoSlider extends React.Component<
     });
   }
 
-  handleReachUp = (clientX, clientY) => {
+  handleReachUp = (clientX: number, clientY: number, touchType: TouchTypeEnum) => {
     const { innerWidth, innerHeight } = window;
-    const { images, onIndexChange, onClose } = this.props;
+    const { images, onIndexChange, onClose, maskClosable = true } = this.props;
     const { lastClientX = clientX, lastClientY = clientY, photoIndex } = this.state;
 
     const offsetClientX = clientX - lastClientX;
@@ -163,7 +166,14 @@ export default class PhotoSlider extends React.Component<
     let currentTranslateX = -singlePageWidth * photoIndex;
     let currentPhotoIndex = photoIndex;
 
-    if (Math.abs(offsetClientY) > innerHeight * 0.14) {
+    // mask 点击事件
+    if (lastClientX === clientX
+      && lastClientY === clientY
+      && maskClosable
+      && touchType === TouchTypeEnum.Mask
+    ) {
+      onClose();
+    } else if (Math.abs(offsetClientY) > innerHeight * 0.14) {
       onClose();
       // 下一张
     } else if (offsetClientX < -maxMoveOffset && photoIndex < images.length - 1) {
@@ -200,6 +210,7 @@ export default class PhotoSlider extends React.Component<
       maskClassName,
       viewClassName,
       imageClassName,
+      onClose,
       loadingElement,
       brokenElement,
     } = this.props;
@@ -222,6 +233,10 @@ export default class PhotoSlider extends React.Component<
             className={maskClassName}
             style={{ background: `rgba(0, 0, 0, ${backdropOpacity})` }}
           />
+          <TopBar>
+            <Counter>{photoIndex + 1} / {imageLength}</Counter>
+            <Close onClick={onClose} />
+          </TopBar>
           {images
             .slice( // 加载相邻三张
               Math.max(photoIndex - 1, 0),

+ 30 - 30
src/PhotoView.tsx

@@ -8,14 +8,8 @@ import getMultipleTouchPosition from './utils/getMultipleTouchPosition';
 import getPositionOnMoveOrScale from './utils/getPositionOnMoveOrScale';
 import slideToSuitableOffset from './utils/slideToSuitableOffset';
 import { getClosedHorizontal, getClosedVertical } from './utils/getCloseEdge';
-import {
-  minReachOffset,
-  minScale,
-  maxScale,
-  scaleBuffer,
-} from './variables';
-
-type ReachFunction = (clientX: number, clientY: number) => void;
+import { maxScale, minReachOffset, minScale, scaleBuffer } from './variables';
+import { ReachFunction, ReachTypeEnum, TouchTypeEnum } from './types';
 
 interface IPhotoViewProps {
   // 图片地址
@@ -42,7 +36,7 @@ interface IPhotoViewProps {
   // 到达左部滑动事件
   onReachLeftMove?: ReachFunction;
   // 触摸解除事件
-  onReachUp?: ReachFunction;
+  onReachUp?: (clientX: number, clientY: number, TouchTypeEnum) => void;
 
   onPhotoResize?: () => void;
 }
@@ -74,8 +68,8 @@ const initialState = {
   // 多指触控间距
   lastTouchLength: 0,
 
-  // 当前边缘触发状态,0: 未触发,1: x 轴,2: y 轴
-  reachState: 0,
+  // 当前边缘触发状态
+  reachState: ReachTypeEnum.Normal,
 };
 
 export default class PhotoView extends React.Component<
@@ -143,7 +137,7 @@ export default class PhotoView extends React.Component<
       let currentX = x;
       let currentY = y;
       // 边缘状态
-      let currentReachState = 0;
+      let currentReachState = ReachTypeEnum.Normal;
       if (touchLength === 0) {
         currentX = newClientX - clientX + lastX;
         currentY = newClientY - clientY + lastY;
@@ -158,9 +152,9 @@ export default class PhotoView extends React.Component<
         );
       }
       // 横向边缘触发、背景触发禁用当前滑动
-      if (currentReachState === 1 || maskTouched) {
+      if (currentReachState === ReachTypeEnum.XReach || maskTouched) {
         this.setState({
-          reachState: 1,
+          reachState: ReachTypeEnum.XReach,
         });
       } else {
         // 目标倍数
@@ -306,7 +300,13 @@ export default class PhotoView extends React.Component<
         clientY,
       }) => {
         if (onReachUp) {
-          onReachUp(newClientX, newClientY);
+          onReachUp(
+            newClientX,
+            newClientY,
+            maskTouched
+              ? TouchTypeEnum.Mask
+              : TouchTypeEnum.Image,
+          );
         }
         const hasMove = clientX !== newClientX || clientY !== newClientY;
         // 缩放弹性效果
@@ -321,7 +321,7 @@ export default class PhotoView extends React.Component<
           touched: false,
           maskTouched: false,
           scale: toScale,
-          reachState: 0, // 重置触发状态
+          reachState: ReachTypeEnum.Normal, // 重置触发状态
           ...hasMove
             ? slideToSuitableOffset({
               x,
@@ -365,7 +365,7 @@ export default class PhotoView extends React.Component<
     scale: number,
     newClientX: number,
     newClientY: number,
-    reachState: number,
+    reachState: ReachTypeEnum,
   ): number => {
     const { width, height } = this.photoRef.state;
 
@@ -382,40 +382,40 @@ export default class PhotoView extends React.Component<
       onReachLeftMove
       && (horizontalType
       && x > minReachOffset
-      && reachState === 0
-      || reachState === 1)
+      && reachState === ReachTypeEnum.Normal
+      || reachState === ReachTypeEnum.XReach)
     ) {
       onReachLeftMove(newClientX, newClientY);
-      return 1;
+      return ReachTypeEnum.XReach;
     } else if (
       onReachRightMove
       && (horizontalType
       && x < -minReachOffset
-      && reachState === 0
-      || reachState === 1)
+      && reachState === ReachTypeEnum.Normal
+      || reachState === ReachTypeEnum.XReach)
     ) {
       onReachRightMove(newClientX, newClientY);
-      return 1;
+      return ReachTypeEnum.XReach;
     } else if (
       onReachTopMove
       && (verticalType
       && y > minReachOffset
-      && reachState === 0
-      || reachState === 2)
+      && reachState === ReachTypeEnum.Normal
+      || reachState === ReachTypeEnum.YReach)
     ) {
       onReachTopMove(newClientX, newClientY);
-      return 2;
+      return ReachTypeEnum.YReach;
     } else if (
       onReachBottomMove
       && (verticalType
       && y < -minReachOffset
-      && reachState === 0
-      || reachState === 2)
+      && reachState === ReachTypeEnum.Normal
+      || reachState === ReachTypeEnum.YReach)
     ) {
       onReachBottomMove(newClientX, newClientY);
-      return 2;
+      return ReachTypeEnum.YReach;
     }
-    return 0;
+    return ReachTypeEnum.Normal;
   }
 
   handlePhotoRef = (ref) => {

+ 3 - 1
src/components/SlideWrap.tsx

@@ -56,7 +56,9 @@ export default class SlideWrap extends React.Component<{
     const { className, children } = this.props;
 
     return createPortal(
-      <Container className={className}>{children}</Container>,
+      <Container className={className}>
+        {children}
+      </Container>,
       this.dialogNode,
     );
   }

+ 53 - 0
src/components/TopWrap.tsx

@@ -0,0 +1,53 @@
+import React from 'react';
+import styled from 'styled-components';
+
+export const TopBar = styled.div`
+  position: absolute;
+  left: 0;
+  top: 0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 100%;
+  height: 44px;
+  color: white;
+  background-color: rgba(0, 0, 0, 0.5);
+  transition: opacity 0.4s linear;
+  will-change: opacity;
+  z-index: 20;
+`;
+
+export const Counter = styled.div`
+  padding: 0 10px;
+  font-size: 14px;
+  opacity: 0.75;
+`;
+
+function CloseSVG(props) {
+  return (
+    <svg
+      version="1.1"
+      xmlns="http://www.w3.org/2000/svg"
+      width="44"
+      height="44"
+      viewBox="0 0 768 768"
+      {...props}
+    >
+      <path
+        fill="#FFF"
+        d="M607.5 205.5l-178.5 178.5 178.5 178.5-45 45-178.5-178.5-178.5 178.5-45-45 178.5-178.5-178.5-178.5 45-45 178.5 178.5 178.5-178.5z"
+      />
+    </svg>
+  );
+}
+
+export const Close = styled(CloseSVG)<React.HTMLAttributes<any>>`
+  padding: 10px;
+  opacity: 0.75;
+  cursor: pointer;
+  transition: opacity 0.2s linear;
+
+  &:hover {
+    opacity: 1;
+  }
+`;

+ 29 - 0
src/types.ts

@@ -5,3 +5,32 @@ export type dataType = {
   dataKey: string;
   src: string;
 };
+
+export type ReachFunction = (clientX: number, clientY: number) => void;
+
+/**
+ * 边缘超出状态
+ */
+export enum CloseEdgeEnum {
+  Normal, // 正常滑动
+  Small, // 小于屏幕宽度
+  Before, // 抵触左边/上边
+  After, // 抵触右边/下边
+}
+
+/**
+ * 边缘触发状态
+ */
+export enum ReachTypeEnum {
+  Normal, // 未触发
+  XReach, // x 轴
+  YReach, // y 轴
+}
+
+/**
+ * 触摸类型
+ */
+export enum TouchTypeEnum {
+  Image = 1, // 图片
+  Mask, // 背景
+}

+ 12 - 10
src/utils/getCloseEdge.ts

@@ -1,3 +1,5 @@
+import { CloseEdgeEnum } from '../types';
+
 /**
  * 接触左边或右边边缘
  * @param x
@@ -9,19 +11,19 @@ export const getClosedHorizontal = (
   x: number,
   scale: number,
   width: number,
-): number => {
+): CloseEdgeEnum => {
   const { innerWidth } = window;
   const currentWidth = width * scale;
   // 图片超出的宽度
   const outOffsetX = (currentWidth - innerWidth) / 2;
   if (currentWidth <= innerWidth) {
-    return 1;
+    return CloseEdgeEnum.Small;
   } else if (x > 0 && outOffsetX - x <= 0) {
-    return 2;
+    return CloseEdgeEnum.Before;
   } else if (x < 0 && outOffsetX + x <= 0) {
-    return 3;
+    return CloseEdgeEnum.After;
   }
-  return 0;
+  return CloseEdgeEnum.Normal;
 };
 
 /**
@@ -35,18 +37,18 @@ export const getClosedVertical = (
   y: number,
   scale: number,
   height: number,
-): number => {
+): CloseEdgeEnum => {
   const { innerHeight } = window;
   const currentHeight = height * scale;
   // 图片超出的高度
   const outOffsetY = (currentHeight - innerHeight) / 2;
 
   if (currentHeight <= innerHeight) {
-    return 1;
+    return CloseEdgeEnum.Small;
   } else if (y > 0 && outOffsetY - y <= 0) {
-    return 2;
+    return CloseEdgeEnum.Before;
   } else if (y < 0 && outOffsetY + y <= 0) {
-    return 3;
+    return CloseEdgeEnum.After;
   }
-  return 0;
+  return CloseEdgeEnum.Normal;
 };

+ 7 - 6
src/utils/slideToSuitableOffset.ts

@@ -1,5 +1,6 @@
 import slideToPosition from './slideToPosition';
 import { getClosedHorizontal, getClosedVertical } from './getCloseEdge';
+import { CloseEdgeEnum } from '../types';
 
 /**
  * 适应到合适的图片偏移量
@@ -47,19 +48,19 @@ const slideToSuitableOffset = ({
   const verticalType = getClosedVertical(endY, scale, height);
 
   // x
-  if (horizontalType === 1) {
+  if (horizontalType === CloseEdgeEnum.Small) {
     currentX = 0;
-  } else if (horizontalType === 2) {
+  } else if (horizontalType === CloseEdgeEnum.Before) {
     currentX = outOffsetX;
-  } else if (horizontalType === 3) {
+  } else if (horizontalType === CloseEdgeEnum.After) {
     currentX = -outOffsetX;
   }
   // y
-  if (verticalType === 1) {
+  if (verticalType === CloseEdgeEnum.Small) {
     currentY = 0;
-  } else if (verticalType === 2) {
+  } else if (verticalType === CloseEdgeEnum.Before) {
     currentY = outOffsetY;
-  } else if (verticalType === 3) {
+  } else if (verticalType === CloseEdgeEnum.After) {
     currentY = -outOffsetY;
   }
 

+ 1 - 1
src/variables.ts

@@ -21,7 +21,7 @@ export const minReachOffset: number = 20;
 /**
  * 默认背景透明度
  */
-export const defaultOpacity: number = 0.8;
+export const defaultOpacity: number = 1;
 
 /**
  * 最小缩放度