React threefibre zooming from zoombar and scroll - reactjs

I have a 3d scene built with react threefibre and I'm able to zoom in and out with both mousewheel and a DOM eleemnt zoombar in this scene.
the demo of the scene can be seen here:
https://codesandbox.io/s/zooming-using-multiple-inputs-lub391
right now, when you zoom with mousewheel, zoombar stays the same, but I want the it change dependant on the mousewheel.
note that I don't want to use mouse wheel event because I may have more sources that also change the zoom and I want the zoombar to be dependant on them too. so the proper way is to add an event listener on camera movement. I wasn't able to do that because whatever I do makes an endless loop.

Perhaps you could use an EventListener on the identified Canvas that listens for mousewheel events like so:
useEffect(() => {
document.getElementById('map-canvas')?.addEventListener('mousewheel', onMouseWheel, false)
return () => {
document.getElementById('map-canvas')?.removeEventListener('mousewheel', onMouseWheel, false)
}
}, [])
return (
<Canvas id="map-canvas"></Canvas>
)
The onMouseWheel function adds the desired value to the previously created context.

Related

Zooming in React ThreeJS scene with zoombar and wheel scroll

I am designing a 3D scene by react-three fiber, and I want to be able to zoom in on the scene via multiple sources (I'm using a zoom Bar and mouse wheel scrolling for zooming now, but other sources may be added in the future like buttons for zoom in and out). To do so, I am using a global state ZoomAmount variable which is a DOM element, and in the onchange callback of the bar, this variable is changed:
const handleChange = () => {
setZoomAmount(refZoomBar.current.value)
}
then I made a hook for zoomAmount state using the react useEffect, so I can change camera.position.z (changing this makes zoom effect)
useEffect(() => {
camera.position.z = ZoomAmount
}, [ZoomAmount])
The problem is that I want the value of the zoombar to change when I zoom in and out using another input (in this case the mouse wheel scroll,) but when I try to implement this feature in the same way by using hooks, the code enters a loop since these elements are both affected by the same value.
How can I do this without facing this issue?
I have put a running demo of my problem here:
https://codesandbox.io/s/zooming-using-multiple-inputs-lub391
I wasn't able to fix this issue but here is what I have tried:
I implemented two hooks
First, a controls eventlistener to set the ZoomAmount whenever camera changed and second, a hook for zoombar to listen to ZoomAmount value change
controls.current?.addEventListener('change', () => {
ZoomAmount = camera.position.z
})
useEffect(()=>{
refZoomBar.current.value = ZoomAmount
},[ZoomAmount])
the above codes are not included in the Sandbox code since they make the program misbehave
This is where the infinite loop starts since ZoomAmount gets changed if the value of camera.position.z is changed and then camera.position.z gets changed again because it is hooked to ZoomAmount.
I also tried using two different states for ZoomAmount but it only makes the loop bigger

onDragEnd not fired when viewport is left

I have found the layer remains in dragmove state when a it is dragged out of the viewport and the mouse button is released. Did you experience this kind of issue?
Have a look over here: https://codesandbox.io/s/llxq3yv829?file=/index.js
Drag the red rectangle out of the viewport and it remains sticky to the cursor.
Thank you!
Set a global event for mouseleave
Use useState to set a state at app level. This can be passed down to components relying on the mouse being in the viewport. Then trigger something to "release" the dragged object.
document.addEventListener("mouseleave", (event) => {
setState({inViewport: false})
}
In the example pass the state to the component.

How do I check when the user has stopped scrolling?

I am using https://facebook.github.io/react-native/docs/scrollview.html, which has a handy onScroll prop, but how do I get when the user has stopped scrolling?
Basically, I'd like to hide a button when the user is scrolling, show it when they aren't.
In your component state, store the last time at which you've received a scroll event. In your render method, check when the last scroll event happened and decide whether your button should become visible again or not.
Rough example:
// Observe the scroll events.
<ScrollView onScroll={(e) => {
this.setState({lastScroll: new Date()})
}} />
// Check if the last scroll happened later than 300ms ago.
if (this.state.lastScroll.getTime() < (new Date()).getTime() - 300) {
// Render the button.
}

React Native: How to disable navigator sceneconfig gestures on the fly?

I know it's possible to disable the scene gesture when pushing the scene with a sceneconfig where gestures are null like so:
return {
...CustomNavigatorSceneConfigs.FloatFromBottom,
gestures: {}
};
But I'd like to temporarily disable the gesture when the view is already pushed.
I have a lightbox/image modal with zoom support. When the image is zoomed-in I need to disable the swipe gesture as it will otherwise activate when the user is panning the image. But by default and zoomed-out I want the gesture to work.
Is it possible to disable the sceneconfig gesture on the fly - like in response to the current state?
My workaround right now is to handle the gesture logic inside the view instead (with panresponder/scrollview events) but since navigator routes aren't transparent (#4494) it's not possible to replicate the default animation/gesture this way.

ExtJS 4.0.7 scrollTo() scrolls but doesn't move scroll bar slider?

I have a tree panel and am trying to do an animated scroll to certain locations. I'm doing something like this:
myTreePanel.getView().getEl().scrollTo('top', yCoord, true /*animate*/);
The view scrolls to the correct location, but the "slider" in the scroll bar doesn't move. Any ideas why?
Some additional info: if I do the following instead, the scrollbar slider moves correctly (but of course scroll() doesn't support animation--I'd prefer to use .scrollTo() so the user can see the scrolling):
myTreePanel.getView().getEl().scroll('down', yDiff);
Thanks for any help/advice!
#MoleculeMan's suggestion of disabling the custom scrollbars (which ExtJS uses in 4.0.x but not in 4.1) does work. After doing this you can call myTreePanel.getView().getEl().scrollTo('top', yCoord, true) and everything works as expected: scrolling is animated and the scrollbar moves. The only problem is that it seems to break the ability for the view to scroll if you use the up/down arrow keys to move through the tree.
It's not very elegant, but the work-around I'm going to use is this:
// Animated scroll of tree view
myTreePanel.getView().getEl().scrollTo('top', yCoord, true);
// Wait 300ms then sync the scroll bar with the tree view
setTimeout(function() {
myTreePanel.setScrollTop(yCoord);
}, 300);
This has the cosmetic disadvantage of the scroll bar "jumping" into place instead of smoothly moving with the animation, but the benefit of not breaking the up/down key scrolling. Also, because it doesn't involve changing config params or overriding the tree view's style, I'm assuming it will still work once we upgrade to ExtJS 4.1 (i.e., the timer call to setScrollTop() will be unnecessary but shouldn't break anything).
Note that calling setScrollTop() moves the scrollbar, but also causes the view to "jump" to that position. You therefore need to ensure that the timer doesn't fire until after the animation finishes. I'm actually using some custom code to poll every 10ms and see if the destination row is visible, then calling setScrollTop(), instead of using a timer that always waits for some hard-coded amount of time:
var scrollToRowNum = 5;
var scrollToEl = getElementForNode(myTreePanel.getRootNode().childNodes[scrollToRowNum]);
var yCoord = scrollToEl.getOffsetsTo(scrollToEl.parent())[1];
// Animated scroll of tree view
myTreePanel.getView().getEl().scrollTo('top', yCoord, true);
// Check every 10ms to see if animation is done, then sync scrollbar
var timerId = setInterval(function() {
if( myTreePanel.isTreeElementWithinVisibleArea(scrollToEl) ) {
clearInterval(timerId);
myTreePanel.setScrollTop(yCoord);
}
}, 10);
The isTreeElementWithinVisibleArea() function just checks to see if element's current Y coordinate (absolute) is between the top and bottom of the tree view box.
Not surprising. They use their own scrollbar. The correct code would be:
myTreePanel.verticalScroller.setScrollTop(yCoord);
However, it doesn't support animation either. So I recommend to get rid of custom scrollbar as I've described here and use your original code.

Resources