Skip to content

Commit

Permalink
feat: 支持自定义工具栏、旋转 API
Browse files Browse the repository at this point in the history
  • Loading branch information
MinJieLiu committed May 5, 2020
1 parent db0b998 commit 4c06315
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 79 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

## 开始使用

Expand Down Expand Up @@ -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 |
Expand Down
69 changes: 68 additions & 1 deletion example/src/index.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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) {}
Expand Down
10 changes: 9 additions & 1 deletion example/src/stories/Test.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Button,
DefaultImage,
ControlledView,

WithToolbar,
IPhotoProviderForwardProps,
IPhotoConsumerForwardProps,
IPhotoSliderForwardProps,
Expand Down Expand Up @@ -91,6 +91,14 @@ import defaultPhoto from '../default-photo.svg';
</Story>
</Preview>

## 自定义工具栏

<Preview>
<Story name="自定义工具栏">
<WithToolbar />
</Story>
</Preview>

# Props

### PhotoProvider
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-photo-view",
"version": "0.4.5",
"version": "0.5.0",
"description": "一款精致的 React 的图片预览组件",
"author": "MinJieLiu",
"license": "MIT",
Expand Down
4 changes: 3 additions & 1 deletion src/Photo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,6 +24,7 @@ const Photo: React.FC<IPhotoProps> = ({
broken,
width,
height,
rotate,
className,
onImageLoad,
loadingElement,
Expand All @@ -38,7 +40,7 @@ const Photo: React.FC<IPhotoProps> = ({
loaded: true,
naturalWidth,
naturalHeight,
...getSuitableImageSize(naturalWidth, naturalHeight),
...getSuitableImageSize(naturalWidth, naturalHeight, rotate),
});
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/PhotoSlider.less
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
43 changes: 31 additions & 12 deletions src/PhotoSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -46,6 +46,8 @@ type PhotoSliderState = {
overlayVisible: boolean;
// 可下拉关闭
canPullClose: boolean;
// 旋转集合
rotatingMap: Map<number, number>;
};

export default class PhotoSlider extends React.Component<IPhotoSliderProps, PhotoSliderState> {
Expand Down Expand Up @@ -83,6 +85,8 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
lastBackdropOpacity: defaultOpacity,
overlayVisible: true,
canPullClose: true,

rotatingMap: new Map<number, number>(),
};
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -298,6 +310,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
bannerVisible,
introVisible,
overlayRender,
toolbarRender,
loadingElement,
brokenElement,
} = this.props;
Expand All @@ -308,6 +321,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
backdropOpacity,
lastBackdropOpacity,
overlayVisible,
rotatingMap,
shouldTransition,
} = this.state;
const imageLength = images.length;
Expand All @@ -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(
Expand All @@ -335,6 +359,7 @@ export default class PhotoSlider extends React.Component<IPhotoSliderProps, Phot
className,
)}
role="dialog"
id="PhotoView_Slider"
onClick={e => e.stopPropagation()}
>
<div
Expand All @@ -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>
)}
Expand Down Expand Up @@ -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}
/>
);
})}
Expand All @@ -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>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/PhotoView.less
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
}

&__PhotoBox {
display: flex;
justify-content: center;
align-items: center;
width: 0;
height: 0;
}

&__PhotoMask {
Expand Down
Loading

0 comments on commit 4c06315

Please sign in to comment.