|
@@ -2,50 +2,46 @@ import React from 'react';
|
|
|
import styled from 'styled-components';
|
|
|
import throttle from 'lodash.throttle';
|
|
|
import Photo from './Photo';
|
|
|
+import { PhotoContainer, Backdrop } from './StyledElements';
|
|
|
+import { slideToPosition, jumpToSuitableOffset } from './util';
|
|
|
+import { animationDefault, animationTimeBase } from './variables';
|
|
|
|
|
|
export interface IPhotoViewProps {
|
|
|
src: string;
|
|
|
}
|
|
|
|
|
|
type PhotoViewState = {
|
|
|
+ // 图片 X 偏移量
|
|
|
x: number;
|
|
|
+ // 图片 y 偏移量
|
|
|
y: number;
|
|
|
+ // 图片缩放程度
|
|
|
scale: number;
|
|
|
+ // 图片处于触摸的状态
|
|
|
touched: boolean;
|
|
|
-
|
|
|
+ // 触摸开始时 x 原始坐标
|
|
|
pageX: number;
|
|
|
+ // 触摸开始时 y 原始坐标
|
|
|
pageY: number;
|
|
|
+ // 触摸开始时图片 x 偏移量
|
|
|
offsetX: number;
|
|
|
+ // 触摸开始时图片 y 偏移量
|
|
|
offsetY: number;
|
|
|
+ // 触摸开始时时间
|
|
|
+ touchedTime: number;
|
|
|
+ // 动画名称
|
|
|
+ animationName: string | null;
|
|
|
+ // 动画时间
|
|
|
+ animationTime: number;
|
|
|
};
|
|
|
|
|
|
-interface DragPhotoProps extends React.HTMLAttributes<any> {}
|
|
|
-
|
|
|
-const PhotoContainer = styled.section`
|
|
|
- position: fixed;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- z-index: 2000;
|
|
|
- overflow: hidden;
|
|
|
-`;
|
|
|
-
|
|
|
-const Backdrop = styled.div`
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- background: rgba(0, 0, 0, 0.4);
|
|
|
- z-index: -1;
|
|
|
-`;
|
|
|
-
|
|
|
-const DragPhoto = styled(Photo)<DragPhotoProps>`
|
|
|
+const DragPhoto = styled(Photo)<React.HTMLAttributes<any>>`
|
|
|
will-change: transform;
|
|
|
+ cursor: -webkit-grab;
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ cursor: -webkit-grabbing;
|
|
|
+ }
|
|
|
`;
|
|
|
|
|
|
export default class PhotoView extends React.Component<
|
|
@@ -62,8 +58,13 @@ export default class PhotoView extends React.Component<
|
|
|
pageY: 0,
|
|
|
offsetX: 0,
|
|
|
offsetY: 0,
|
|
|
+ touchedTime: 0,
|
|
|
+ animationName: animationDefault,
|
|
|
+ animationTime: animationTimeBase,
|
|
|
};
|
|
|
|
|
|
+ private photoRef;
|
|
|
+
|
|
|
constructor(props) {
|
|
|
super(props);
|
|
|
this.handleMove = throttle(this.handleMove, 8);
|
|
@@ -91,21 +92,26 @@ export default class PhotoView extends React.Component<
|
|
|
pageY,
|
|
|
offsetX: prevState.x,
|
|
|
offsetY: prevState.y,
|
|
|
+ touchedTime: Date.now(),
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
- handleMove = (pageX, pageY) => {
|
|
|
+ handleMove = (newPageX, newPageY) => {
|
|
|
if (this.state.touched) {
|
|
|
- this.setState((prevState) => ({
|
|
|
- x: pageX - prevState.pageX + prevState.offsetX,
|
|
|
- y: pageY - prevState.pageY + prevState.offsetY,
|
|
|
- }));
|
|
|
+ this.setState(({ pageX, pageY, offsetX, offsetY }) => {
|
|
|
+ return {
|
|
|
+ x: newPageX - pageX + offsetX,
|
|
|
+ y: newPageY - pageY + offsetY,
|
|
|
+ };
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
|
handleDoubleClick = () => {
|
|
|
this.setState(prevState => ({
|
|
|
- scale: prevState.scale > 1 ? 1 : 2,
|
|
|
+ scale: prevState.scale > 1 ? 1 : 3,
|
|
|
+ animationName: animationDefault,
|
|
|
+ animationTime: animationTimeBase,
|
|
|
}));
|
|
|
}
|
|
|
|
|
@@ -128,14 +134,40 @@ export default class PhotoView extends React.Component<
|
|
|
}
|
|
|
|
|
|
handleMouseUp = () => {
|
|
|
- this.setState({
|
|
|
- touched: false,
|
|
|
+ const { width, height } = this.photoRef.state;
|
|
|
+ this.setState(({
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ offsetX,
|
|
|
+ offsetY,
|
|
|
+ scale,
|
|
|
+ touchedTime,
|
|
|
+ }) => {
|
|
|
+ return {
|
|
|
+ touched: false,
|
|
|
+ ...jumpToSuitableOffset({
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ scale,
|
|
|
+ ...slideToPosition({
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ offsetX,
|
|
|
+ offsetY,
|
|
|
+ touchedTime,
|
|
|
+ }),
|
|
|
+ }),
|
|
|
+ };
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ handlePhotoRef = (ref) => {
|
|
|
+ this.photoRef = ref;
|
|
|
+ }
|
|
|
+
|
|
|
render() {
|
|
|
const { src } = this.props;
|
|
|
- const { x, y, scale } = this.state;
|
|
|
+ const { x, y, scale, touched, animationName, animationTime } = this.state;
|
|
|
const transform = `translate3d(${x}px, ${y}px, 0) scale(${scale})`;
|
|
|
|
|
|
return (
|
|
@@ -143,12 +175,16 @@ export default class PhotoView extends React.Component<
|
|
|
<Backdrop />
|
|
|
<DragPhoto
|
|
|
src={src}
|
|
|
+ innerRef={this.handlePhotoRef}
|
|
|
onDoubleClick={this.handleDoubleClick}
|
|
|
onMouseDown={this.handleMouseDown}
|
|
|
onTouchStart={this.handleTouchStart}
|
|
|
style={{
|
|
|
WebkitTransform: transform,
|
|
|
transform,
|
|
|
+ transition: touched
|
|
|
+ ? undefined
|
|
|
+ : `transform ${animationTime}ms ${animationName}`,
|
|
|
}}
|
|
|
/>
|
|
|
</PhotoContainer>
|