Weāre really in love with the Needle.Tools homepage, and weāre using it as a guideline for a similar experience weāre creating for a client. To our nieve understanding, the Needle.Tools homepage is relying on something like ScrollFollow to drive a Timeline to animate something like a ViewBox, but then at any time, the user is free to interact with the camera through something like OrbitControls. But when we try to put these things together, things seem to get out of sync pretty quickly. Is there a way for a scroll event to communicate something like a āresetā to OrbitControls such that the userās perspective is renormalized. Otherwise, we keep ending up in situations in which the userās perspective is stuck behind a wall, even as the scrolling continues to animate the ViewBox.
Hi Aaron, thanks for the question. Combining scrollādriven animations (using ScrollFollow and ViewBox) with manual camera control (via OrbitControls) can indeed lead to state conflicts. Currently there isnāt a built-in mechanism for a scroll event to automatically āresetā OrbitControls, so youāll need to implement a sync strategy yourself.
A common approach is to create a custom Needle component that listens for scroll events and then updates OrbitControls accordinglyāfor example, by resetting its internal state and updating its target so that it matches the ViewBoxās intended framing. The idea is that once the scroll event is processed (or once scrolling stops), you update OrbitControls.target (using, say, a target value provided by the ViewBox) and then call OrbitControls.reset() so it aligns with the new camera settings. You might also want to debounce these events for a smooth transition.
Hereās a sample implementation in TypeScript:
import { Behaviour, serializable } from "@needle-tools/engine";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { ViewBox } from "@needle-tools/engine";
export class ScrollOrbitSync extends Behaviour {
@serializable(OrbitControls)
orbitControls?: OrbitControls;
@serializable(ViewBox)
viewBox?: ViewBox;
private lastScrollTime = 0;
onEnable() {
window.addEventListener("scroll", this.handleScroll.bind(this));
}
onDisable() {
window.removeEventListener("scroll", this.handleScroll.bind(this));
}
handleScroll() {
this.lastScrollTime = performance.now();
// Debounce resetting controls after scroll pauses (adjust debounce time as needed)
setTimeout(() => {
if (performance.now() - this.lastScrollTime >= 100 && this.orbitControls) {
// Optionally, if your ViewBox provides a computed target or custom method,
// update the OrbitControls target to match it.
// For example, assuming a getTarget() method exists:
const newTarget = this.viewBox && (this.viewBox as any).getTarget ? (this.viewBox as any).getTarget() : { x: 0, y: 0, z: 0 };
this.orbitControls.target.set(newTarget.x, newTarget.y, newTarget.z);
// Reset the OrbitControls state to synchronize its internal state with the new target.
this.orbitControls.reset();
}
}, 100);
}
}
This custom component listens for scroll events and, after a brief debounce, resets OrbitControls so that its target aligns with the ViewBox settings. Keep in mind you may need to tweak the debounce time and the method by which you acquire the ādesiredā target position. This approach helps counteract issues like having the camera view āstuckā behind objects even though the scroll animation continues.
Additionally, note that recent updates to the ViewBox component have improved its evaluation order relative to OrbitControls (see changelogs like those in at CHANGELOG.md and at ViewBox.d.ts.md), so make sure youāre using the latest version for best results.
I hope this helps you synchronize your scroll-driven framing and manual camera interactions. Let me know if you need further adjustments or if you have any more questions!
1 replies left - Become PRO for more replies.
thread_M1fGGM09q31dEDFuYPR9A3pp
Reply to this post directly if you need further help (bottom right button)
The AI does not appear to know about ScrollFollow or ViewBox.
Hello @Aaron_Collegeman
Iād suggest you take a look at GitHub - needle-engine/needle-engine-bike-scrollytelling which is using ViewBox, ScrollFollow and Timeline.
What the needle.tools website does differently is using Splines using the SplineWalker component. This became part of Needle Engine core around version ~4.9.
(I think currently ScrollFollow does evaluate the timeline every frame and not just when scrolling which is different from the implementation the needle.tools website uses.)
Do you have an example link that I could look at?
Hello @Aaron_Collegeman
Iām working on a sample for ScrollFollow, ViewBox and OrbitControls + Splines.
Itās now released in samples package 1.3.1
This topic was automatically closed 13 days after the last reply. New replies are no longer allowed.