Skip to content

Commit

Permalink
fix: onScale API 调用后缩放位置不准确的问题
Browse files Browse the repository at this point in the history
fix: 调整图片移除后不保留 `scale` 和 `rotate` 状态
  • Loading branch information
MinJieLiu committed Mar 13, 2022
1 parent a2e0758 commit 3a43679
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 128 deletions.
12 changes: 9 additions & 3 deletions packages/example/components/doc-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ export default function DocDemo() {
className={`flex-none flex flex-col items-center justify-center bg-white ${attrs.className}`}
>
<div style={{ transform: `scale(${childScale})` }}>
<div>Hello world</div>
<Button>button</Button>
<input onMouseDown={(e) => e.stopPropagation()} />
<div className="mb-2">Hello world</div>
<Button className="mb-2 w-full" primary>
button
</Button>
<input
className="border border-gray-300 h-8 p-2"
placeholder="Input"
onMouseDown={(e) => e.stopPropagation()}
/>
</div>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions packages/example/pages/docs/change-log.en-US.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
title: Change Log
---

## 1.0.0-beta.8

1. fix: Inaccurate zoom position after `onScale` API call
2. fix: `scale` and `rotate` states are not preserved after resizing image removal

## 1.0.0-beta.7

1. fix: Long image mode bug fixes and new examples
Expand Down
5 changes: 5 additions & 0 deletions packages/example/pages/docs/change-log.zh-CN.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
title: Change Log
---

## 1.0.0-beta.8

1. fix: `onScale` API 调用后缩放位置不准确的问题
2. fix: 调整图片移除后不保留 `scale``rotate` 状态

## 1.0.0-beta.7

1. fix: 长图模式 BUG 修复及新增示例
Expand Down
2 changes: 1 addition & 1 deletion packages/react-photo-view/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-photo-view",
"version": "1.0.0-beta.7",
"version": "1.0.0-beta.8",
"description": "An exquisite React photo preview component",
"author": "MinJieLiu",
"license": "Apache-2.0",
Expand Down
133 changes: 79 additions & 54 deletions packages/react-photo-view/src/PhotoBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import getMultipleTouchPosition from './utils/getMultipleTouchPosition';
import getPositionOnMoveOrScale from './utils/getPositionOnMoveOrScale';
import { getReachType, computePositionEdge } from './utils/edgeHandle';
import getRotateSize from './utils/getRotateSize';
import { minStartTouchOffset, scaleBuffer } from './variables';
import type { DataType, ReachMoveFunction, ReachFunction, PhotoTapFunction, BrokenElementParams } from './types';
import type { ReachType, TouchStartType } from './types';
import useSetState from './hooks/useSetState';
import { limitScale } from './utils/limitTarget';
import getSuitableImageSize from './utils/getSuitableImageSize';
import useIsomorphicLayoutEffect from './hooks/useIsomorphicLayoutEffect';
import { minStartTouchOffset, scaleBuffer } from './variables';
import type {
DataType,
ReachMoveFunction,
ReachFunction,
PhotoTapFunction,
BrokenElementParams,
ExposedProperties,
} from './types';
import type { ReachType, TouchStartType } from './types';
import useSetState from './hooks/useSetState';
import useMethods from './hooks/useMethods';
import useDebounceCallback from './hooks/useDebounceCallback';
import useEventListener from './hooks/useEventListener';
import useContinuousTap from './hooks/useContinuousTap';
Expand Down Expand Up @@ -40,10 +48,6 @@ export interface PhotoBoxProps {
loadingElement?: JSX.Element;
// 加载失败 Element
brokenElement?: JSX.Element | ((photoProps: BrokenElementParams) => JSX.Element);
// 旋转状态
rotate?: number;
// 放大缩小
scale?: number;

// Photo 点击事件
onPhotoTap: PhotoTapFunction;
Expand All @@ -55,8 +59,8 @@ export interface PhotoBoxProps {
onReachUp: ReachFunction;
// Resize 事件
onPhotoResize: () => void;
// 滚轮事件
onWheel: (scale: number) => void;
// 向父组件导出属性
expose: (state: ExposedProperties) => void;
// 是否在当前操作中
isActive: boolean;
}
Expand All @@ -67,9 +71,9 @@ const initialState = {
// 真实高度
naturalHeight: undefined as number | undefined,
// 宽度
width: 0,
width: undefined as number | undefined,
// 高度
height: 0,
height: undefined as number | undefined,
// 加载成功状态
loaded: undefined as boolean | undefined,
// 破碎状态
Expand All @@ -83,6 +87,10 @@ const initialState = {
touched: false,
// 背景处于触摸状态
maskTouched: false,
// 旋转状态
rotate: 0,
// 放大缩小
scale: 1,

// 触摸开始时 x 原始坐标
CX: 0,
Expand All @@ -97,6 +105,8 @@ const initialState = {
lastCX: 0,
// 上一个触摸状态 y 原始坐标
lastCY: 0,
// 上一个触摸状态的 scale
lastScale: 1,

// 触摸开始时时间
touchTime: 0,
Expand All @@ -111,7 +121,7 @@ const initialState = {
};

export default function PhotoBox({
item: { src, render, width: customWidth, height: customHeight, originRef },
item: { src, render, width: customWidth = 0, height: customHeight = 0, originRef },
visible,
speed,
easing,
Expand All @@ -120,45 +130,68 @@ export default function PhotoBox({
style,
loadingElement,
brokenElement,
rotate = 0,
scale = 1,

onPhotoTap,
onMaskTap,
onReachMove,
onReachUp,
onPhotoResize,
onWheel,
isActive,
expose,
}: PhotoBoxProps) {
const [state, updateState] = useSetState(initialState);
const initialTouchRef = useRef<TouchStartType>(0);
const mounted = useMountedRef();

const {
naturalWidth = customWidth || 0,
naturalHeight = customHeight || 0,
width,
height,
naturalWidth = customWidth,
naturalHeight = customHeight,
width = customWidth,
height = customHeight,
loaded = !src,
broken,
x,
y,
touched,
stopRaf,
maskTouched,
rotate,
scale,
CX,
CY,
lastX,
lastY,
lastCX,
lastCY,
lastScale,
touchTime,
touchLength,
pause,
reach,
} = state;

const fn = useMethods({
onScale: (current: number) => onScale(limitScale(current)),
onRotate(current: number) {
if (rotate !== current) {
expose({ rotate: current });
updateState({ rotate: current, ...getSuitableImageSize(naturalWidth, naturalHeight, current) });
}
},
});

// 默认为屏幕中心缩放
function onScale(current: number, clientX?: number, clientY?: number) {
if (scale !== current) {
expose({ scale: current });
updateState({
scale: current,
...getPositionOnMoveOrScale(x, y, width, height, scale, current, clientX, clientY),
...(current <= 1 && { x: 0, y: 0 }),
});
}
}

const handleMove = useDebounceCallback(
(nextClientX: number, nextClientY: number, currentTouchLength: number = 0) => {
if ((touched || maskTouched) && isActive) {
Expand Down Expand Up @@ -207,21 +240,21 @@ export default function PhotoBox({
naturalWidth / width,
scaleBuffer,
);
if (scale !== toScale) {
onWheel(toScale);
}
// 导出变量
expose({ scale: toScale });
updateState({
touchLength: currentTouchLength,
reach: currentReach,
scale: toScale,
...getPositionOnMoveOrScale(
x,
y,
nextClientX,
nextClientY,
width,
height,
scale,
toScale,
nextClientX,
nextClientY,
offsetX,
offsetY,
),
Expand Down Expand Up @@ -250,34 +283,26 @@ export default function PhotoBox({
(nextY) => updateRaf({ y: nextY }),
(nextScale) => {
if (mounted.current) {
onWheel(nextScale);
expose({ scale: nextScale });
updateState({ scale: nextScale });
}
return !touched && mounted.current;
},
);

const handlePhotoTap = useContinuousTap(onPhotoTap, (currentClientX: number, currentClientY: number) => {
if (reach !== undefined) {
return;
if (!reach) {
// 若图片足够大,则放大适应的倍数
const endScale = scale !== 1 ? 1 : Math.max(2, naturalWidth / width);
onScale(endScale, currentClientX, currentClientY);
}
// 若图片足够大,则放大适应的倍数
const endScale = scale !== 1 ? 1 : Math.max(2, naturalWidth / width);
onWheel(endScale);
updateState({
...getPositionOnMoveOrScale(x, y, currentClientX, currentClientY, width, height, scale, endScale),
...(endScale <= 1 && { x: 0, y: 0 }),
});
});

function handleUp(nextClientX: number, nextClientY: number) {
// 重置响应状态
initialTouchRef.current = 0;
if ((touched || maskTouched) && isActive) {
const hasMove = CX !== nextClientX || CY !== nextClientY;
const toScale = limitScale(scale, naturalWidth / width || 1);
if (toScale !== scale) {
onWheel(toScale);
}
updateState({
touched: false,
maskTouched: false,
Expand All @@ -286,7 +311,7 @@ export default function PhotoBox({
reach: undefined,
});
// Go
slideToPosition(x, y, lastX, lastY, width, height, scale, rotate, touchTime);
slideToPosition(x, y, lastX, lastY, width, height, scale, lastScale, rotate, touchTime);

onReachUp(nextClientX, nextClientY);
// 触发 Tap 事件
Expand Down Expand Up @@ -338,8 +363,10 @@ export default function PhotoBox({
);

useIsomorphicLayoutEffect(() => {
updateState(getSuitableImageSize(naturalWidth, naturalHeight, rotate));
}, [rotate]);
if (isActive) {
expose({ scale, rotate, ...fn });
}
}, [isActive]);

function handlePhotoLoad(params: IPhotoLoadedParams) {
updateState({
Expand All @@ -357,25 +384,23 @@ export default function PhotoBox({
lastCY: currentClientY,
lastX: x,
lastY: y,
lastScale: scale,
touchLength: currentTouchLength,
touchTime: Date.now(),
});
}

function handleWheel(e: React.WheelEvent) {
if (reach !== undefined) {
return;
if (!reach) {
// 限制最大倍数和最小倍数
const toScale = limitScale(scale - e.deltaY / 100 / 2, naturalWidth / width);
updateState({
CX: e.clientX,
CY: e.clientY,
stopRaf: true,
});
onScale(toScale, e.clientX, e.clientY);
}
// 限制最大倍数和最小倍数
const toScale = limitScale(scale - e.deltaY / 100 / 2, naturalWidth / width);
updateState({
CX: e.clientX,
CY: e.clientY,
stopRaf: true,
...getPositionOnMoveOrScale(x, y, e.clientX, e.clientY, width, height, scale, toScale),
...(toScale <= 1 && { x: 0, y: 0 }),
});
onWheel(toScale);
}

function handleMaskStart(e: { clientX: number; clientY: number }) {
Expand Down
Loading

1 comment on commit 3a43679

@vercel
Copy link

@vercel vercel bot commented on 3a43679 Mar 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.