Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canvas view is skewed when using ScrollControls #2081

Open
mirker21 opened this issue Aug 29, 2024 · 2 comments
Open

Canvas view is skewed when using ScrollControls #2081

mirker21 opened this issue Aug 29, 2024 · 2 comments

Comments

@mirker21
Copy link

Hello!

I am currently using
"@react-three/drei": "^9.111.3",
"@react-three/fiber": "^8.17.5",
"three": "^0.167.1"

I am having an issue with the canvas in r3f. When I implemented ScrollControls on the camera, the environment and all the items in the scene appear to be stretched out vertically, and would return to normal as soon as I scrolled to the bottom. Also, the Environment takes up the whole canvas when PerspectiveCamera is used, unlike Orthographic camera, but I originally intended to use OrthographicCamera.

Here is my example of the problem I am encountering. I would like the skewed effect to not happen, and for the camera’s animation to happen instead. If you know what might be causing this issue, please let me know!

Thank you!

@mirker21 mirker21 reopened this Aug 29, 2024
@CodyJasonBennett CodyJasonBennett transferred this issue from pmndrs/react-three-fiber Aug 29, 2024
@mirker21
Copy link
Author

mirker21 commented Aug 31, 2024

Update: as mentioned in this post, adding a useLayoutEffect helps a little bit. However, I'm not able to control the aspect that well so that cameraRef.current remains consistently unstretched. If I set the aspect to a decimal, it doesn't look stretched, but it stretches when I scroll closer to the bottom. Also the useLayoutEffect only seems to trigger whenever I resize the window. I was expecting scroll.offset to trigger this aspect change, but it doesn't.

@mirker21
Copy link
Author

mirker21 commented Sep 2, 2024

Update: I found a hacky sort of solution, I created a state that was changed to the scrollTop value using the onWheel event handler on the , and I passed this value onto the camera where I had the state be a dependency in useLayoutEffect.

For the equation for calculating the correct aspect, I wrote down x and y values (x = scrollTop, y=aspect) starting from the top and plugged them into here, which gave me an equation to use in useLayoutEffect to update the aspect more accurately.

The onWheel event listener doesn't detect every minute wheel movement though, so there are small moments here and there that the aspect has a slight stretch effect before it corrects itself, but at least it is easier to look at now.

Here is my code:

ScrollControls Canvas Component:

import { ScrollControls } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import Camera from "./3D_models/3D_intro_components/Camera"
import { Suspense, useRef, useState } from "react";

export default function WebpageContentIntro3D(): JSX.Element {
    const [scrollTop, setScrollTop] = useState(0);
    const scrollRef = useRef<any>('div#webpage-intro-3d')

    function handleScroll() {
        if (scrollRef.current !== null) {
            setScrollTop(scrollRef.current.children[0].children[0].children[1].scrollTop)
        }
    }
    
    return (
        <div id="webpage-intro-3d" ref={scrollRef} onWheel={() => handleScroll()}>
            <Canvas id="webpage-intro-3d-canvas">
                <ambientLight intensity={4} color={0xFFFFFF} />

                <mesh position={[0,0,0]}>
                    <meshStandardMaterial color='red' />
                    <boxGeometry args={[1,1,1]} />
                </mesh>

                <Suspense fallback={null}>
                    <ScrollControls pages={20} damping={0.2} maxSpeed={0.25}>
                        <Camera scrollTop={scrollTop}/>
                    </ScrollControls>
                </Suspense>
            </Canvas>
        </div>
    )
}

Scroll Camera Component:

import * as THREE from 'three'
import React, { useLayoutEffect, useRef } from 'react'
import { useEffect } from 'react'
import { useFrame, useThree } from '@react-three/fiber'
import { useGLTF, useAnimations, useScroll, PerspectiveCamera, Scroll, OrthographicCamera } from '@react-three/drei'
import { GLTF } from 'three-stdlib'

type ActionName = 'Animation'

interface GLTFAction extends THREE.AnimationClip {
  name: ActionName
}

type GLTFResult = GLTF & {
  nodes: {}
  materials: {}
  animations: GLTFAction[]
}

export default function Camera({ scrollTop }: { scrollTop: number }) {
  const group = React.useRef<THREE.Group>(null!)
  const cameraRef = useRef<any>(null!);
  const { nodes, materials, animations } = useGLTF('/3D_Assets/3D_Intro_Component_Models/camera-transformed.glb') as GLTFResult
  const { actions } = useAnimations(animations, group)
  const {viewport} = useThree()
  const scroll = useScroll()
  useEffect(() => void (actions.Animation!.reset().play().paused = true), [])
  useFrame(() => {
    actions.Animation!.time = actions.Animation!.getClip().duration * scroll.offset;
  })
  
  useLayoutEffect(() => {
    cameraRef.current.aspect = 1.5 * scroll.offset + 0.15;
    cameraRef.current.updateProjectionMatrix()
  }, [viewport, scrollTop])
  return (
    <Scroll>
      <group ref={group} dispose={null}>
        <group name="Scene">
          <PerspectiveCamera makeDefault ref={cameraRef} name="Camera" far={50000} near={0.001} position={[-1.154, 17.732, 15.619]} scale={[0.007, .007, 0.001]} />
        </group>
      </group>
    </Scroll>
  )
}

useGLTF.preload('/3D_Assets/3D_Intro_Component_Models/camera-transformed.glb')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant