From 9a84a7f0235681143475df94fbaf75204537be13 Mon Sep 17 00:00:00 2001 From: Kieran Mann Date: Tue, 12 Jul 2022 13:34:29 -0700 Subject: [PATCH 1/3] fix: view component supports non-fullscreen for v8.1.0, logs warning if older version detected --- src/web/View.tsx | 57 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/web/View.tsx b/src/web/View.tsx index 93c60e40c..9440f4e4f 100644 --- a/src/web/View.tsx +++ b/src/web/View.tsx @@ -6,6 +6,26 @@ const isOrthographicCamera = (def: any): def is THREE.OrthographicCamera => def && (def as THREE.OrthographicCamera).isOrthographicCamera const col = new THREE.Color() +/** + * In `@react-three/fiber` after `v8.0.0` but prior to `v8.1.0`, `state.size` contained only dimension + * information. After `v8.1.0`, position information (`top`, `left`) was added + * + * @todo remove this when drei supports v9 and up + */ +type LegacyCanvasSize = { + height: number + width: number +} + +type CanvasSize = LegacyCanvasSize & { + top: number + left: number +} + +function isNonLegacyCanvasSize(size: Record): size is CanvasSize { + return 'top' in size +} + export type ContainerProps = { scene: THREE.Scene index: number @@ -13,7 +33,7 @@ export type ContainerProps = { frames: number rect: React.MutableRefObject track: React.MutableRefObject - canvasSize: Size + canvasSize: LegacyCanvasSize | CanvasSize } export type ViewProps = { @@ -27,6 +47,23 @@ export type ViewProps = { children?: React.ReactNode } +function computeContainerPosition(canvasSize: LegacyCanvasSize | CanvasSize, trackRect: DOMRect): any { + const { right, top, left: trackLeft, bottom: trackBottom, width, height } = trackRect + + if (isNonLegacyCanvasSize(canvasSize)) { + const canvasBottom = canvasSize.top + canvasSize.height + const bottom = canvasBottom - trackBottom + const left = trackLeft - canvasSize.left + + return { left, right, top, bottom, width, height } + } + + // Fall back on old behavior if r3f < 8.1.0 + const bottom = canvasSize.height - trackBottom + + return { left: trackLeft, right, top, bottom, width, height } +} + function Container({ canvasSize, scene, index, children, frames, rect, track }: ContainerProps) { const get = useThree((state) => state.get) const camera = useThree((state) => state.camera) @@ -41,9 +78,9 @@ function Container({ canvasSize, scene, index, children, frames, rect, track }: } if (rect.current) { - const { left, right, top, bottom, width, height } = rect.current + const { left, right, top, bottom, width, height } = computeContainerPosition(canvasSize, rect.current) const isOffscreen = bottom < 0 || top > canvasSize.height || right < 0 || left > canvasSize.width - const positiveYUpBottom = canvasSize.height - bottom + const aspect = width / height if (isOrthographicCamera(camera)) { @@ -61,8 +98,8 @@ function Container({ canvasSize, scene, index, children, frames, rect, track }: camera.updateProjectionMatrix() } - state.gl.setViewport(left, positiveYUpBottom, width, height) - state.gl.setScissor(left, positiveYUpBottom, width, height) + state.gl.setViewport(left, bottom, width, height) + state.gl.setScissor(left, bottom, width, height) state.gl.setScissorTest(true) if (isOffscreen) { @@ -84,6 +121,16 @@ function Container({ canvasSize, scene, index, children, frames, rect, track }: return () => setEvents({ connected: old }) }, []) + React.useEffect(() => { + if (isNonLegacyCanvasSize(canvasSize)) { + return + } + console.warn( + 'Detected @react-three/fiber canvas size does not include position information. may not work as expected. ' + + 'Upgrade to @react-three/fiber ^8.1.0 for support.\n See https://github.com/pmndrs/drei/issues/944' + ) + }, []) + return <>{children} } From b36ddd5af46e04b60ba5b72e99edb754273bf512 Mon Sep 17 00:00:00 2001 From: Kieran Mann Date: Tue, 12 Jul 2022 13:41:19 -0700 Subject: [PATCH 2/3] docs: add callout to README doc for View --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dcd8911e9..84ee21cb4 100644 --- a/README.md +++ b/README.md @@ -1844,7 +1844,13 @@ useBVH(mesh) Views use gl.scissor to cut the viewport into segments. You tie a view to a tracking div which then controls the position and bounds of the viewport. This allows you to have multiple views with a single, performant canvas. These views will follow their tracking elements, scroll along, resize, etc. -It is advisable to re-connect the event system to a parent that contains both the canvas and the html content. This ensures that both are accessible/selectable and even allows you to mount controls or other deeper integrations into your view. +It is advisable to re-connect the event system to a parent that contains both the canvas and the html content. +This ensures that both are accessible/selectable and even allows you to mount controls or other deeper +integrations into your view. + +> Note that `@react-three/fiber` newer than `^8.1.0` is required for `View` to work correctly if the +> canvas/react three fiber root is not fullscreen. A warning will be logged if drei is used with older +> versions of `@react-three/fiber`. ```tsx Date: Thu, 28 Jul 2022 13:59:22 -0700 Subject: [PATCH 3/3] fix: speculatively fix offscreen computation --- src/web/View.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/web/View.tsx b/src/web/View.tsx index 9440f4e4f..715bcc8e7 100644 --- a/src/web/View.tsx +++ b/src/web/View.tsx @@ -47,21 +47,28 @@ export type ViewProps = { children?: React.ReactNode } -function computeContainerPosition(canvasSize: LegacyCanvasSize | CanvasSize, trackRect: DOMRect): any { +function computeContainerPosition( + canvasSize: LegacyCanvasSize | CanvasSize, + trackRect: DOMRect +): { + position: CanvasSize & { bottom: number, right: number } + isOffscreen: boolean +} { const { right, top, left: trackLeft, bottom: trackBottom, width, height } = trackRect - + const isOffscreen = trackRect.bottom < 0 || top > canvasSize.height || right < 0 || trackRect.left > canvasSize.width + if (isNonLegacyCanvasSize(canvasSize)) { const canvasBottom = canvasSize.top + canvasSize.height const bottom = canvasBottom - trackBottom const left = trackLeft - canvasSize.left - return { left, right, top, bottom, width, height } + return { position: { width, height, left, top, bottom, right }, isOffscreen } } // Fall back on old behavior if r3f < 8.1.0 const bottom = canvasSize.height - trackBottom - return { left: trackLeft, right, top, bottom, width, height } + return { position: { width, height, top, left: trackLeft, bottom, right }, isOffscreen } } function Container({ canvasSize, scene, index, children, frames, rect, track }: ContainerProps) { @@ -78,8 +85,10 @@ function Container({ canvasSize, scene, index, children, frames, rect, track }: } if (rect.current) { - const { left, right, top, bottom, width, height } = computeContainerPosition(canvasSize, rect.current) - const isOffscreen = bottom < 0 || top > canvasSize.height || right < 0 || left > canvasSize.width + const { + position: { left, bottom, width, height }, + isOffscreen, + } = computeContainerPosition(canvasSize, rect.current) const aspect = width / height