Skip to content

feat: CameraControls support for 1:1 events onX callback props #2451

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

Merged
merged 5 commits into from
May 29, 2025

Conversation

abernier
Copy link
Member

@abernier abernier commented May 28, 2025

supports all onX https://github.com/yomotsu/camera-controls/tree/dev?tab=readme-ov-file#events

Event name Timing
'controlstart' When the user starts to control the camera via mouse / touches. ¹
'control' When the user controls the camera (dragging).
'controlend' When the user ends to control the camera. ¹
'transitionstart' When any kind of transition starts, either user control or using a method with enableTransition = true
'update' When the camera position is updated.
'wake' When the camera starts moving.
'rest' When the camera movement is below .restThreshold ².
'sleep' When the camera end moving.
  • backward compat for onStart/onEnd/onChange
  • Documentation updated (example)
  • Storybook entry added (example)
  • Ready to be merged

🐛 it also fixes a bug with frameloop="demand"

where calling programmatic CameraControls methods caused a jerky animation

Before:

frameloop-before.mp4

After:

frameloop-after.mp4

Copy link

vercel bot commented May 28, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
drei ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 28, 2025 10:07pm

Copy link

codesandbox-ci bot commented May 28, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@abernier abernier changed the title feat: CameraControls 1:1 events callbacks feat: CameraControls support for 1:1 events onX callbacks May 28, 2025
@abernier abernier changed the title feat: CameraControls support for 1:1 events onX callbacks feat: CameraControls support for 1:1 events onX callback props May 28, 2025
@FrostKiwi
Copy link

FrostKiwi commented May 29, 2025

OMG yes please <3
Literally the main reason I don't have demand enabled.

I tried out this PR and the problem remains. Specifically, I have the camera controlled via setLookAt. I changed the rendering loop to demand. After rendering pauses because of no state updates and I call setLookAt, CameraControls simply skips 95% of the smoothing path and teleports to the destination.

Installed this PR via npm i https://pkg.csb.dev/pmndrs/drei/commit/20225955/@react-three/drei as instructed by https://ci.codesandbox.io/status/pmndrs/drei/pr/2451/builds/620661 and package-lock.json reports for drei:

"node_modules/@react-three/drei": {
    "version": "0.0.0-semantic-release",
    "resolved": "https://pkg.csb.dev/pmndrs/drei/commit/20225955/@react-three/drei",
    "integrity": "sha512-csYgbtSOdZQJdLKg5GSU2iz4eBXh1Z2AQN1zpeEcg2TbG9GNnuGQdCAHgpmhDGM/r9sb4ZMOtMs17Lsbz4H8sg==",
    "license": "MIT",
    "dependencies": {
        "@babel/runtime": "^7.26.0",
        "@mediapipe/tasks-vision": "0.10.17",
        "@monogrid/gainmap-js": "^3.0.6",
        "@use-gesture/react": "^10.3.1",
        "camera-controls": "^2.9.0",
        "cross-env": "^7.0.3",
        "detect-gpu": "^5.0.56",
        "glsl-noise": "^0.0.0",
        "hls.js": "^1.5.17",
        "maath": "^0.10.8",
        "meshline": "^3.3.1",
        "stats-gl": "^2.2.8",
        "stats.js": "^0.17.0",
        "suspend-react": "^0.1.3",
        "three-mesh-bvh": "^0.8.3",
        "three-stdlib": "^2.35.6",
        "troika-three-text": "^0.52.4",
        "tunnel-rat": "^0.1.2",
        "use-sync-external-store": "^1.4.0",
        "utility-types": "^3.11.0",
        "zustand": "^5.0.1"
    },
    "peerDependencies": {
        "@react-three/fiber": "^9.0.0",
        "react": "^19",
        "react-dom": "^19",
        "three": ">=0.159"
    },
    "peerDependenciesMeta": {
        "react-dom": {
            "optional": true
        }
    }
},

and here my package.json dependencies for context:

package.json dependencies
"dependencies": {
    "@react-three/drei": "https://pkg.csb.dev/pmndrs/drei/commit/20225955/@react-three/drei",
    "@react-three/fiber": "^9.1.0",
    "r3f-perf": "^7.2.3",
    "react": "^19.0.0",
    "react-colorful": "^5.6.1",
    "react-dom": "^19.0.0",
    "sass": "^1.86.0",
    "three": "^0.176.0",
    "vite-plugin-mkcert": "^1.17.8",
    "zustand": "^5.0.3"
  },
  "devDependencies": {
    "@eslint/js": "^9.21.0",
    "@gltf-transform/cli": "^4.1.3",
    "@types/react": "^19.0.10",
    "@types/react-dom": "^19.0.4",
    "@vitejs/plugin-react-swc": "^3.8.0",
    "eslint": "^9.21.0",
    "eslint-plugin-react-hooks": "^5.1.0",
    "eslint-plugin-react-refresh": "^0.4.19",
    "globals": "^16.0.0",
    "gltfjsx": "^6.5.3",
    "vite": "^6.2.0"
  }

@abernier abernier merged commit af7c22f into master May 29, 2025
5 checks passed
@abernier abernier deleted the camera-controls-1-1-events-callbacks branch May 29, 2025 09:33
Copy link

🎉 This PR is included in version 10.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@abernier
Copy link
Member Author

@FrostKiwi i have a clue looking at https://yomotsu.github.io/camera-controls/examples/rest-and-sleep.html:

with that new release, can you try

<CameraControls
  onTransitionStart={() => invalidate()}
/>

and tell me if it solves the issue with frameloop="demand"?

if yes i'll create a new pr for

@abernier
Copy link
Member Author

abernier commented May 29, 2025

@FrostKiwi even without this trick @react-three/drei@latest solves the previous jerky anim issues i had

same for you?

@FrostKiwi
Copy link

@FrostKiwi i have a clue looking at https://yomotsu.github.io/camera-controls/examples/rest-and-sleep.html:

with that new release, can you try

<CameraControls
  onTransitionStart={() => invalidate()}
/>

and tell me if it solves the issue with frameloop="demand"?

No, it does not solve the problem. The camera still teleports to the destination. There was never a missing invalidate, CameraControls already handles it. The underlying issue is the huge DeltaTime given to smoothing operations of CameraControls, which results in setLookAt() simply skipping forward in time.

if yes i'll create a new pr for

That would be wonderful ❤

@react-three/drei@latest solves the previous jerky anim issues i had, same for you?

Yes, specifically when starting to interact with the camera, the very first movement used to result in a jerk, something that looked like a frame stutter, something I reported as I think it also affects normal camera rotation as well, introducing a small hitch that gets mostly unnoticed. in yomotsu/camera-controls#508 (comment) . Now comparing both version, it is indeed gone!

Still, the camera teleports big distances because of #2005 (comment) in functions setLookAt() and similar functions.

@abernier
Copy link
Member Author

abernier commented May 30, 2025

Still, the camera teleports big distances because of #2005 (comment) in functions setLookAt() and similar functions.

does userland's https://r3f.docs.pmnd.rs/advanced/scaling-performance#sync-animations-with-on-demand-rendering-and-invalidate prevent the issue?

if yes it's probably good enough, right?

@FrostKiwi
Copy link

FrostKiwi commented Jun 2, 2025

does userland's https://r3f.docs.pmnd.rs/advanced/scaling-performance#sync-animations-with-on-demand-rendering-and-invalidate prevent the issue?

if yes it's probably good enough, right?

Yes and no.
Indeed, following the doc I can go through all my code and patch every usage of stuff that animates from controls.setLookAt(-2, 1.5, 3, 0.470086, 1.16085, 0, true) to invalidate(); requestAnimationFrame(() => controls.setLookAt(-2, 1.5, 3, 0.470086, 1.16085, 0, true));.

But that doesn't cover all cases and AFAIK, that leaves one special case out: Scrollwheel, which is handled specially. Scrollwheel always results in small teleportation.
So... am I missunderstanding something?: We can't use CameraControls events like wake as they fire too late. You have to go lower level, below React Three Fiber and hook into canvas to catch scroll events Events and issue an invalidate + requestAnimationFrame combo? I'm smelling a race condition here.

From the doc: https://github.com/yomotsu/camera-controls/blob/dev/readme.md#events

mouseButtons.wheel (Mouse wheel control) does not emit 'controlstart' and 'controlend'. mouseButtons.wheel uses scroll-event internally, and scroll-event happens intermittently. That means "start" and "end" cannot be detected.

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

Successfully merging this pull request may close these issues.

2 participants