mirror of
https://github.com/wavetermdev/docusaurus-og.git
synced 2026-08-05 13:46:35 -07:00
feat: enable image/diagram pinch zoom on mobile devices
This commit is contained in:
@@ -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)')
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user