function ZoomBack(){
const [close, setclose] = useState(false)
const api = useBounds();
useEffect(() => {
api.refresh().fit(); // just zoom back.. focus on whole scene
}, [close])
return (
<h1 onClick={()=>{setclose(true)}}>
Zoomback
</h1>
)
}
How can I use the useBounds outside of the canvas scene structure? I just want to have a DOM element, a button which controls the useBounds…
Why I want this: When I use the
then each of the elements in group are zoomable and when I click outside of one of those elements, it zooms back. Exactyl the zoomback functionality I need it on a click event of a DOM element.
usebounds is contextual to its bounds provider, outside of that it can’t function. in my opinion reaching internals upwards in general is an anti pattern. an application should always flow top down unidirectionally.
a state model should contain simple, logical, serialisable information, a [x, y, z] vector that points towards where you want to zoom, or the id of the object that’s supposed to be in focus (better than the vector imo), etc. any component can now react to that. once you start to always divide the app between state and view everything will become easier.
in the beginning i struggled, i was trying to reach the objects that needed to change color up just ending up with spaghetti. i stopped and chose a global state model, all problems vanished.
thank you, I understand that you say, I should have something like a state management on top on everything (you used valtio for that). But I still don’t get it how I can pass the useBounds context to valtios proxy so that I can access it from elsewhere. I tried something like this:
const state = proxy({
current: null,
api: null,
})
export default function App() {
const api = useBounds()
const snap = useSnapshot(state)
state.api = api
return ( <ZoomBack></ZoomBack> ..
...
function ZoomBack() {
const snap = useSnapshot(state)
return (
<h1
onClick={() => {
snap.api.refresh().fit()
}}>
Zoomback
</h1>
)
}
but snap.api seems to be null - I think I am doing it wrong hehe
you dont need a state manager, could also just be a useState. whatever you use, i would not reach up a context into state, which is what i was saying - app flow should be top down, not down up. the state should just be an id, or a string: { active: “headphones” }. each item could listen to that:
function Model({ name }) {
const bounds = useBounds()
const current = useStore(state => state.current) // zustand, for instance ...
useEffect(() => {
// Each model will zoom onto itself if the global state indicates that it's in focus
if (current === name) bounds.doThisDoThat(...)
}, [current])
...
you reach up a canvas internal that just shouldn’t leak, i would consider it dirty even if you can make it work. the h1 should have no knowledge of “threejs”, “bounds” and other canvas stuff. the state model simply keeps a minimal description of app flow.
I would say I have exactly what is in your second example. The parent which manages its children; buuut the problem in your example is exactly what I try to solve: you used in useEffect bounds.doThisDoThat() and exactly that is “sometimes” not working!
Cannot read properties of null (reading ‘updateWorldMatrix’)
Have a quick look in my example, it is set up the same way like your second example; somehow the call to useBounds().refresh().fit(); is only working when it’s called on a THREE object (for example onClick of the object) but not on the js code for example with a timeout or in useEffect.
I think I did it correctly, with managing the sate of the zoom status and on click of the DOM h1/button I change the sate and in the SelectToZoom I subscribe to the zoom status and react…