Skip to content

Commit

Permalink
Merge 518b6ec into 486e0f1
Browse files Browse the repository at this point in the history
  • Loading branch information
wanpan11 authored Sep 25, 2023
2 parents 486e0f1 + 518b6ec commit 9d50449
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 3 deletions.
10 changes: 10 additions & 0 deletions assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,13 @@
height: 100%;
pointer-events: none;

&-content {
height: 100%;
}

&-body {
.box;
height: 100%;
overflow: hidden;
}

Expand All @@ -85,6 +90,8 @@
z-index: @zindex-preview-mask;
height: 100%;
background-color: fade(@preview-mask-bg, 45%);
touch-action: none;
user-select: none;

&-hidden {
display: none;
Expand Down Expand Up @@ -130,6 +137,9 @@
z-index: @zindex-preview-mask;
overflow: auto;
outline: 0;
touch-action: none;
user-select: none;
height: 100%;
-webkit-overflow-scrolling: touch;
}

Expand Down
3 changes: 3 additions & 0 deletions docs/examples/basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const icons = {
export default function Base() {
return (
<div>
{/* eslint-disable-next-line global-require */}
<Image src={`${require('./images/min.png')}`} />

<Image
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
width={200}
Expand Down
Binary file added docs/examples/images/min.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 26 additions & 1 deletion src/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import getFixScaleEleTransPosition from './getFixScaleEleTransPosition';
import type { TransformAction, TransformType } from './hooks/useImageTransform';
import useImageTransform from './hooks/useImageTransform';
import useStatus from './hooks/useStatus';
import useTouchZoom from './hooks/useTouchZoom';
import Operations from './Operations';
import { BASE_SCALE_RATIO, WHEEL_MAX_SCALE_RATIO } from './previewConfig';

Expand Down Expand Up @@ -149,6 +150,16 @@ const Preview: React.FC<PreviewProps> = props => {
[`${prefixCls}-moving`]: isMoving,
});

// touch
const { touchPointInfo, onTouchStart, onTouchMove, onTouchEnd, onTouchCancel } = useTouchZoom(
updateTransform,
dispatchZoomChange,
transform,
visible,
imgRef,
prefixCls,
);

useEffect(() => {
if (!enableTransition) {
setEnableTransition(true);
Expand Down Expand Up @@ -233,6 +244,8 @@ const Preview: React.FC<PreviewProps> = props => {
};

const onMouseDown: React.MouseEventHandler<HTMLDivElement> = event => {
if (touchPointInfo.eventType !== 'init') return;

// Only allow main button
if (!movable || event.button !== 0) return;
event.preventDefault();
Expand Down Expand Up @@ -283,6 +296,8 @@ const Preview: React.FC<PreviewProps> = props => {
};

const onDoubleClick = (event: React.MouseEvent<HTMLImageElement, MouseEvent>) => {
if (touchPointInfo.eventType !== 'init') return;

if (visible) {
if (scale !== 1) {
updateTransform({ x: 0, y: 0, scale: 1 }, 'doubleClick');
Expand Down Expand Up @@ -350,13 +365,23 @@ const Preview: React.FC<PreviewProps> = props => {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0) scale3d(${
transform.flipX ? '-' : ''
}${scale}, ${transform.flipY ? '-' : ''}${scale}, 1) rotate(${rotate}deg)`,
transitionDuration: !enableTransition && '0s',
transitionDuration:
!enableTransition || touchPointInfo.eventType !== 'init'
? !enableTransition
? '0'
: '60ms'

Check warning on line 372 in src/Preview.tsx

View check run for this annotation

Codecov / codecov/patch

src/Preview.tsx#L372

Added line #L372 was not covered by tests
: undefined,
}}
fallback={fallback}
src={src}
onWheel={onWheel}
onMouseDown={onMouseDown}
onDoubleClick={onDoubleClick}
// touch
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
onTouchCancel={onTouchCancel}
/>
);

Expand Down
14 changes: 12 additions & 2 deletions src/hooks/useImageTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ export type TransformType = {
flipY: boolean;
};

export type Transform = {
x: number;
y: number;
rotate: number;
scale: number;
flipX: boolean;
flipY: boolean;
}

export type TransformAction =
| 'flipY'
| 'flipX'
Expand All @@ -25,9 +34,10 @@ export type TransformAction =
| 'wheel'
| 'doubleClick'
| 'move'
| 'dragRebound';
| 'dragRebound'
| 'touchZoom';

const initialTransform = {
const initialTransform: Transform = {
x: 0,
y: 0,
rotate: 0,
Expand Down
219 changes: 219 additions & 0 deletions src/hooks/useTouchZoom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import { useCallback, useEffect, useRef } from 'react';
import type { Transform, TransformAction, TransformType } from './useImageTransform';

type Point = {
x: number;
y: number;
};
type EventType = 'init' | 'zoom' | 'move';

let lastTouchEnd = 0;
const initPoint = { x: 0, y: 0 };

function getDistance(a: Point, b: Point) {
const x = a.x - b.x;
const y = a.y - b.y;
return Math.hypot(x, y);

Check warning on line 16 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L13-L16

Added lines #L13 - L16 were not covered by tests
}
function getCenter(a: Point, b: Point) {
const x = (a.x + b.x) / 2;
const y = (a.y + b.y) / 2;
return [x, y];

Check warning on line 21 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L18-L21

Added lines #L18 - L21 were not covered by tests
}

/** Prohibit WeChat sliding & Prohibit browser scaling */
function touchstart(event: TouchEvent) {

Check warning on line 25 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L25

Added line #L25 was not covered by tests
if (event.touches.length > 1) {
event.preventDefault();

Check warning on line 27 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L27

Added line #L27 was not covered by tests
}
}
function touchmove(event: TouchEvent) {
event.preventDefault();

Check warning on line 31 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L30-L31

Added lines #L30 - L31 were not covered by tests
}
function touchend(event: TouchEvent) {
const now = Date.now();

Check warning on line 34 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L33-L34

Added lines #L33 - L34 were not covered by tests
if (now - lastTouchEnd <= 300) {
event.preventDefault();

Check warning on line 36 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L36

Added line #L36 was not covered by tests
}
lastTouchEnd = now;

Check warning on line 38 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L38

Added line #L38 was not covered by tests
}
function slidingControl(stop: boolean, className: string) {
const containerEle = document.getElementsByClassName(className)?.[0] as HTMLElement;

if (stop) {
if (containerEle) {
containerEle.parentElement.addEventListener('touchmove', touchmove, {
passive: false,
});
containerEle.addEventListener('touchmove', touchmove, {
passive: false,
});
}

document.addEventListener('touchstart', touchstart, {
passive: false,
});
document.addEventListener('touchend', touchend, {
passive: false,
});
} else {
if (containerEle) {
containerEle.parentElement.removeEventListener('touchmove', touchmove);
containerEle.removeEventListener('touchmove', touchmove);
}

document.removeEventListener('touchstart', touchstart);
document.removeEventListener('touchend', touchend);
}
}

/** Pinch-to-zoom & Move image after zooming in */
export default function useTouchZoom(
updateTransform: (newTransform: Partial<TransformType>, action: TransformAction) => void,
dispatchZoomChange: (
ratio: number,
action: TransformAction,
clientX?: number,
clientY?: number,
) => void,
transform: Transform,
visible: boolean,
imgRef: React.MutableRefObject<HTMLImageElement>,
prefixCls: string,
) {
const { x: translateX, y: translateY, scale } = transform;
const { width: imgWidth, height: imgHeight } = imgRef.current || { width: 0, height: 0 };

const getTranslateLimit = () => {
const offsetX = (imgWidth * scale - document.documentElement.clientWidth) / 2;
const offsetY = (imgHeight * scale - document.documentElement.clientHeight) / 2;
return [offsetX, offsetY];

Check warning on line 90 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L88-L90

Added lines #L88 - L90 were not covered by tests
};

const getOverflow = () => {
return [

Check warning on line 94 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L94

Added line #L94 was not covered by tests
imgWidth * scale > document.documentElement.clientWidth,
imgHeight * scale > document.documentElement.clientHeight,
];
};

const touchPointInfo = useRef<{ touchOne: Point; touchTwo: Point; eventType: EventType }>({
touchOne: { ...initPoint },
touchTwo: { ...initPoint },
eventType: 'init',
});

const setTouchPoint = useCallback((a: Point, b: Point, eventType: EventType) => {
touchPointInfo.current.touchOne = a;
touchPointInfo.current.touchTwo = b;
touchPointInfo.current.eventType = eventType;

Check warning on line 109 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L107-L109

Added lines #L107 - L109 were not covered by tests
}, []);

const restTouchPoint = (event: React.TouchEvent<HTMLImageElement>) => {
const { touches = [] } = event;
if (touches.length) return;

const [offsetX, offsetY] = getTranslateLimit();
const [xOverflow, yOverflow] = getOverflow();

Check warning on line 117 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L116-L117

Added lines #L116 - L117 were not covered by tests

let newX = translateX;
let newY = translateY;

Check warning on line 120 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L119-L120

Added lines #L119 - L120 were not covered by tests

if (xOverflow) {
if (translateX > offsetX) {
newX = offsetX;

Check warning on line 124 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L124

Added line #L124 was not covered by tests
} else if (translateX < -offsetX) {
newX = -offsetX;

Check warning on line 126 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L126

Added line #L126 was not covered by tests
}
} else {
newX = 0;

Check warning on line 129 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L128-L129

Added lines #L128 - L129 were not covered by tests
}

if (yOverflow) {
if (translateY > offsetY) {
newY = offsetY;

Check warning on line 134 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L134

Added line #L134 was not covered by tests
} else if (translateY < -offsetY) {
newY = -offsetY;

Check warning on line 136 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L136

Added line #L136 was not covered by tests
}
} else {
newY = 0;

Check warning on line 139 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L138-L139

Added lines #L138 - L139 were not covered by tests
}

setTimeout(() => {
updateTransform({ x: newX, y: newY }, 'move');

Check warning on line 143 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L142-L143

Added lines #L142 - L143 were not covered by tests
}, 100);

setTouchPoint({ ...initPoint }, { ...initPoint }, 'init');

Check warning on line 146 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L146

Added line #L146 was not covered by tests
};

const onTouchStart = useCallback(
(event: React.TouchEvent<HTMLImageElement>) => {

Check warning on line 150 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L150

Added line #L150 was not covered by tests
const { touches = [] } = event;
if (touches.length > 1) {
// touch zoom
setTouchPoint(

Check warning on line 154 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L154

Added line #L154 was not covered by tests
{ x: touches[0].clientX, y: touches[0].clientY },
{ x: touches[1].clientX, y: touches[1].clientY },
'zoom',
);
} else {

Check warning on line 159 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L159

Added line #L159 was not covered by tests
// touch move
setTouchPoint(

Check warning on line 161 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L161

Added line #L161 was not covered by tests
{
x: touches[0].clientX - transform.x,
y: touches[0].clientY - transform.y,
},
{ ...initPoint },
'move',
);
}
},
[setTouchPoint, transform],
);

const onTouchMove = (event: React.TouchEvent<HTMLImageElement>) => {
const { touches = [] } = event;
const { touchOne, touchTwo, eventType } = touchPointInfo.current;

Check warning on line 176 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L176

Added line #L176 was not covered by tests

const oldPoint = {

Check warning on line 178 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L178

Added line #L178 was not covered by tests
a: { x: touchOne.x, y: touchOne.y },
b: { x: touchTwo.x, y: touchTwo.y },
};
const newPoint = {

Check warning on line 182 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L182

Added line #L182 was not covered by tests
a: { x: touches[0]?.clientX, y: touches[0]?.clientY },
b: { x: touches[1]?.clientX, y: touches[1]?.clientY },
};

if (eventType === 'zoom' && touches.length > 1) {
const [x, y] = getCenter(newPoint.a, newPoint.b);
const ratio = getDistance(newPoint.a, newPoint.b) / getDistance(oldPoint.a, oldPoint.b);

Check warning on line 189 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L188-L189

Added lines #L188 - L189 were not covered by tests

dispatchZoomChange(ratio, 'touchZoom', x, y);
setTouchPoint(newPoint.a, newPoint.b, 'zoom');

Check warning on line 192 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L191-L192

Added lines #L191 - L192 were not covered by tests
} else if (eventType === 'move') {
updateTransform(

Check warning on line 194 in src/hooks/useTouchZoom.ts

View check run for this annotation

Codecov / codecov/patch

src/hooks/useTouchZoom.ts#L194

Added line #L194 was not covered by tests
{
x: newPoint.a.x - oldPoint.a.x,
y: newPoint.a.y - oldPoint.a.y,
},
'move',
);
}
};

useEffect(() => {
if (visible) {
slidingControl(true, prefixCls + '-mask');
} else {
slidingControl(false, prefixCls + '-mask');
}
}, [visible, prefixCls]);

return {
touchPointInfo: touchPointInfo.current,
onTouchStart,
onTouchMove,
onTouchEnd: restTouchPoint,
onTouchCancel: restTouchPoint,
};
}

0 comments on commit 9d50449

Please sign in to comment.