feat: enable image/diagram pinch zoom on mobile devices

This commit is contained in:
Hossein Mehrabi
2023-06-20 13:15:09 +03:30
parent 15312f0fdc
commit 1e3375f061
6 changed files with 128 additions and 31 deletions
@@ -52,6 +52,7 @@
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-quick-pinch-zoom": "^4.9.0",
"react-use": "^17.4.0",
"sharp": "^0.32.1",
"three": "^0.152.2",
@@ -3,16 +3,19 @@ import NavbarLogo from '@theme/Navbar/Logo'
import clsx from 'clsx'
import React, {
CSSProperties,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import QuickPinchZoom, { make3dTransformValue } from 'react-quick-pinch-zoom'
import { useWindowScroll } from 'react-use'
import { IconFullscreen, IconFullscreenExit } from '../../components/Icon/Icon'
import { Portal } from '../../components/Portal/Portal'
import { useHydrated } from '../../lib/useHydrated'
import { useIsMobile } from '../../lib/useIsMobile'
import styles from './styles.module.scss'
export const LightBoxProvider: React.FC<React.PropsWithChildren<{}>> = ({
@@ -25,6 +28,8 @@ export const LightBoxProvider: React.FC<React.PropsWithChildren<{}>> = ({
opacity: '0.5',
})
const isMobile = useIsMobile()
const style: React.CSSProperties = useMemo(() => {
return {
opacity: 1,
@@ -37,7 +42,7 @@ export const LightBoxProvider: React.FC<React.PropsWithChildren<{}>> = ({
setActive(element)
const vw = document.body.clientWidth
const vh = document.body.clientHeight
const vh = window.innerHeight
const maxWidth = window.innerWidth > 768 ? vw * 0.9375 : vw - 32
const maxHeight = vh - 128
@@ -73,6 +78,16 @@ export const LightBoxProvider: React.FC<React.PropsWithChildren<{}>> = ({
if (active && window.innerWidth > 768) close()
}, [scroll])
useEffect(() => {
if (isMobile && active) {
const html = document.querySelector('html')!
html.style.overflow = 'hidden'
} else {
const html = document.querySelector('html')!
html.style.overflow = 'initial'
}
}, [isMobile, active])
return (
<LightBoxContext.Provider
value={{ active, style, activeStyle, display, close, toggle }}
@@ -80,14 +95,19 @@ export const LightBoxProvider: React.FC<React.PropsWithChildren<{}>> = ({
{children}
{hydrated && (
<Portal containerId="lsd-presentation">
<div className={clsx(styles.nav, active && styles.visible)}>
<nav>
<NavbarLogo />
<IconButton size="medium" onClick={close}>
<IconFullscreenExit />
</IconButton>
</nav>
</div>
<>
<div
className={clsx(styles.backdrop, active && styles.visible)}
></div>
<div className={clsx(styles.navWrapper, active && styles.visible)}>
<nav className={clsx(styles.nav, active && styles.visible)}>
<NavbarLogo />
<IconButton size="medium" onClick={close}>
<IconFullscreenExit />
</IconButton>
</nav>
</div>
</>
</Portal>
)}
</LightBoxContext.Provider>
@@ -137,25 +157,57 @@ export const LightBoxWrapper: React.FC<React.PropsWithChildren<{}>> = ({
children,
}) => {
const ref = useRef<HTMLDivElement>(null)
const childRef = useRef<HTMLDivElement>(null)
const { getStyle, display, isActiveElement } = useLightBox()
return (
<div
className={clsx(
styles.wrapper,
isActiveElement(ref.current!) && styles.active,
)}
ref={ref}
style={ref.current ? getStyle(ref.current) : {}}
>
{children}
<IconButton
className={styles.fullscreenButton}
size="medium"
onClick={() => ref.current && display(ref.current)}
const isMobile = useIsMobile()
console.log(isMobile)
const onUpdate = useCallback(({ x, y, scale }) => {
const { current: img } = childRef
if (img) {
const value = make3dTransformValue({ x, y, scale })
img.style.setProperty('transform', value)
}
}, [])
const content =
isMobile && ref.current && isActiveElement(ref.current!) ? (
<QuickPinchZoom
onUpdate={onUpdate}
doubleTapZoomOutOnMaxScale
maxZoom={3}
>
<IconFullscreen />
</IconButton>
</div>
<div ref={childRef}>{children}</div>
</QuickPinchZoom>
) : (
<>
{children}
<IconButton
className={styles.fullscreenButton}
size="medium"
onClick={() => ref.current && display(ref.current)}
>
<IconFullscreen />
</IconButton>
</>
)
return (
<>
<div
className={clsx(
styles.wrapper,
isActiveElement(ref.current!) && styles.active,
)}
ref={ref}
style={ref.current ? getStyle(ref.current) : {}}
>
{content}
</div>
</>
)
}
@@ -1,6 +1,6 @@
@use '../../css/utils';
.nav {
.backdrop {
position: fixed;
top: 0;
left: 0;
@@ -12,12 +12,25 @@
visibility: hidden;
}
.visible {
opacity: 1 !important;
visibility: visible !important;
.navWrapper {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100%;
background: transparent;
z-index: 203;
opacity: 0;
visibility: hidden;
pointer-events: none;
& > * {
pointer-events: initial;
}
}
.nav > nav {
.nav {
display: flex;
flex-direction: row;
align-items: center;
@@ -27,6 +40,12 @@
margin: 0 auto;
padding: var(--ifm-navbar-padding-vertical)
var(--ifm-navbar-padding-horizontal);
background: rgb(var(--lsd-surface-primary));
}
.visible {
opacity: 1 !important;
visibility: visible !important;
}
.wrapper {
@@ -67,3 +86,9 @@
}
}
}
@include utils.responsive('lg', 'down') {
.wrapper > div {
overflow: visible !important;
}
}
@@ -1171,4 +1171,8 @@ table {
--ifm-hero-text-color: rgb(var(--lsd-text-primary));
}
body {
height: auto !important;
}
@import './blog.scss';
@@ -0,0 +1,3 @@
import { useMedia } from 'react-use'
export const useIsMobile = () => useMedia('(max-width: 1199px)')
+12
View File
@@ -14171,6 +14171,13 @@ react-merge-refs@^1.1.0:
resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06"
integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==
react-quick-pinch-zoom@^4.9.0:
version "4.9.0"
resolved "https://registry.yarnpkg.com/react-quick-pinch-zoom/-/react-quick-pinch-zoom-4.9.0.tgz#13d91adb7ea2e75c5a2ff43d71d2a0e04a56cf62"
integrity sha512-gCPnZu5+rkYDNvewi/d7A2wgLs7izQNMuC6kjt+KLC1qqHnRU27Ed8AgcEcnWAZKdMr3ZKoKhSvjlZSVZI0fuw==
dependencies:
tslib ">=2.0.0"
react-reconciler@^0.26.2:
version "0.26.2"
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91"
@@ -16753,6 +16760,11 @@ tslib@2.0.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
tslib@>=2.0.0:
version "2.5.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913"
integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==
tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"