I've a little problem with an animation. I try to do a Flash Bar Info on my app for the errors. So to do that I create a new component, this component can be place in one specific view or not, and when the error is trigger from my store the state of my component is change by componentWillReceiveProps and set to visible if there is an error message.
So, if there isn't an error message my render function return false value but if there is an error message I run my render function with this animation code:
// Ease in ease out anitmation
Animated.sequence([
Animated.timing(this.state.translateY, {
toValue: 40,
easing: Easing.bounce, // Like a ball
duration: 450,
}),
Animated.delay(3000),
Animated.timing(this.state.translateY, {
toValue: 0,
duration: 300,
}),
]).start(() => {
this.props.clearMessage();
this.setState({ visible: false }); // <-- Problem is here
});
If I stay on my view during the animation delay, that's work perfectly, but if I go to my previous view when the message is set to visible, the function setState is called when my component is not mounted.
So I got this waring:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
It's a normal behavior because the component is not mounted at this point.
So, I try to find a way to cancel my animation sequence when the component will unmount.
During my research I find a temporary way by using setTimeout() function with clearTimeout when the component will unmount, but I can't find how to do that with Animated.delay function in an Animated.sequence, is it possible ?
In advance thank you for your answers!
TLDR;
It is possible to cancel an animation delay when component will unmount ?
The function you give to Animated.start() is called with an object declaring whether the animation ran to the end successfully. React-Native also implicitly cancels your animation if the component is unmounted. So check for the finished-property in your callback, and only setState if the animation ran to the end.
So this should work:
...
]).start((done) => {
if (done.finished) {
this.props.clearMessage();
this.setState({ visible: false });
}
});
(Note that if you manually stop the animation, using e.g. Animated.stop() or by starting another animation with the same Animated.Value, the finished-property will also then be false.)
See: https://facebook.github.io/react-native/docs/animated.html#working-with-animations
Related
I'm working on a WebGL application using ThreeJS and OrbitControls. How do I write and make use of a custom event handler for wheel spin events?
Suppose my custom handler is
function custom_handleMouseWheel(event) { ... }
I tried adding it as a listener:
window.addEventListener("wheel", custom_handleMouseWheel);
but I suspect this adds my handler to a list of existing handlers (probably only one), and I'd have to remove the original handler. Not sure how to do that. And anyway, my handler never was called, which I checked for by adding a console.log("Wheel!") line to my handler.
Another thing I tried is to replace the handleWheelMouse method in the controls object, like this:
let original_handleMouseWheel;
function custom_handleMouseWheel(event) {
console.log("Custom Wheel!");
... fancy geometry calculations ...
original_handleMouseWheel(event);
}
// somewhere after defining scene, camera, renderer, etc...
controls = new THREE.OrbitControls( camera );
original_handleMouseWheel = controls.handleMouseWheel;
controls.handleMouseWheel = custom_handleMouseWheel;
but again the console.log line never executes.
What is the right way to go about doing this?
Just setting controls.enableZoom = false will stop the default zoom.
The default "wheel" listener is added to the "domElement" which should be the second parameter sent to the OrbitControls constructor.
Try renderer.domElement.addEventListener('wheel', custom_handleMouseWheel);
I'm having issue getting the React Fabric UI DetailsList to work with React Hooks. It renders, but the selection part does not. Whenever, you select a row, I expect the count of the selection to be updated. However, i'm not seeing that. It looks like the selection component items never get updated even thou the UI shows it being selected. I'm also seeing the onSelectionChange being triggered when you select a row. Now sure if its because i'm using react hooks or what
I took the provided class example which works:
[Codepen]https://codepen.io/pen/?&editable=true (Example)
Same as the original but stripped down
[Codepen]https://codepen.io/darewreckk/pen/NQRWEd?editors=1111
converted it to a react hook component. I would expect the same thing, but i'm not and can't figure out what's different.
Modified Example with Hooks
[Codepen]https://codepen.io/darewreckk/pen/GVjRNb?editors=1111
Any advice appreciated,
Thanks,
Derek
The selection count is stored on the Selection object that you are creating. You could log it out like so:
const _selection = new Selection({
onSelectionChanged: () => {
console.log('Selected count:' + _selection.count);
}
});
You can set the value of selectionCount and selectionDetails whenever onSelectionChanged is called:
const _selection = new Selection({
onSelectionChanged: () => {
setSelectionCount(_selection.count);
setSelectionDetails(`${_selection.count} items selected`)
}
});
As a side note: If you want selectionDetails to update when selectionCount updates, you can use the useEffect hook:
React.useEffect(() => {
setSelectionDetails(`${selectionCount} items selected`);
}, [selectionCount]);
The above effect will run whenever selectionCount updates, and so in turn update selectionDetails.
I have this code:
$("input#drawAllRoutes").click(function (e) {
console.log("drawAllRoutes: Start Drawing");
showWaitPanel();
...
//foreach routeElement add vector layer on map
...
console.log("drawAllRoutes: Ok ");
hideWaitPanel();
})
I would have this behavior:
show wait panel adding the correct class in a div: this is done by showWaitPanel();
after that I add an high number of vector layer in openlayers3 map
when done, the wait panel is set hide with hideWaitPanel() that remove a class from a div
The problem is that with this code, the UI is not rendered because the vectors drawing require more resources and so freeze the UI.
So I don't see the wait panel, and the UI is freezed until the vector layers are drawed on the map.
How can I render the wait panel before the drawings?
I have read about deferred method, but I don't know very well it.
Thanks for any support.
You probably just need to force each stage into a different event thread, which can be achieved in a couple of ways.
Using window.setTimeout()
This is simple and should work despite being syntactically ugly.
$("input#drawAllRoutes").click(function (e) {
console.log("drawAllRoutes: Start Drawing");
showWaitPanel(); // assumed to be synchronous.
window.setTimeout(function() {
...
//foreach routeElement add vector layer on map
...
hideWaitPanel(); // ok in same event thread unless vector rendering is itself asynchronous.
console.log("drawAllRoutes: Ok");
}, 0); // even with a timeout of zero seconds, the passed function will execute in a later event thread.
});
Using a promise
The nett effect here should be very similar to using setTimeout(), but it will work only if showWaitPanel() returns a promise, otherwise showWaitPanel().then() will throw an error. So you would need to amend your showWaitPanel() function.
$("input#drawAllRoutes").click(function (e) {
console.log("drawAllRoutes: Start Drawing");
showWaitPanel().then(function() {
...
//foreach routeElement add vector layer on map
...
hideWaitPanel(); // ok in same event thread unless vector rendering is itself asynchronous.
console.log("drawAllRoutes: Ok");
});
});
TBH, using a promise is overkill here. If it works, I would use setTimeout() despite its ugliness.
In trying to figure out how to start and stop Sprite animations from a SpriteSheet, I tried this:
// other code...
// define animations in SpriteSheet:
"animations": {"intro": [0, 19, "false"]}
// other code...
var spriteSheet = new createjs.SpriteSheet(data);
var intro = new createjs.Sprite(spriteSheet, "intro");
stage.addChild(intro);
createjs.Ticker.addEventListener("tick", stage);
intro.stop();
var btnStart = document.getElementById("btnStart");
btnStart.onclick = function() {
console.log("btnStart clicked");
intro.on("animationend", onStartAnimEnd);
intro.play();
};
function onStartAnimEnd(e) {
intro.removeEventListener("animationend", onStartAnimEnd);
console.log("Start anim ended");
}
In the above example, if the user clicks the btnStart button, the onStartAnimEnd callback fires indefinitely, even though by defining "false" in the animation configuration to signal we want to stop on the last frame, and the animation does in fact stop, and I'm removing the event listener in the first line of the callback.
If I add:
function onStartAnimEnd(e) {
intro.removeEventListener("animationend", onStartAnimEnd);
intro.stop();
console.log("Start anim ended");
}
The problem goes away, but that doesn't seem right... So, if I change the listener assignment of the animationend event from:
intro.on("animationend", onStartAnimEnd);
to:
ask.addEventListener("animationend", onAskAnimEnd);
...and with this change, the indefinite event captures goes away. So my questions are:
What's the difference with these two event listener assignments?
Is this animationend event continually firing in the background because we're updating the stage on the tick event, even though nothing needs re-rendering?
Thanks for your time!
This is actually a duplicated question. As I answered you previous question, your animation definition is wrong, you need to use the boolean value (false) and not the string value ("false").
Now sure what ask is, but the method on is a wrapper for addEventListener, and where you can specify things such as the scope of the callback and if it will run only once. Take a look at the API to know more:
http://www.createjs.com/Docs/EaselJS/classes/EventDispatcher.html#method_on
I am developing an app iOS app in appcelerator and I got a table with user.
When I click on the user it opens the profile but I also want the user to be able to copy the name just by tap and hold for 2 seconds.
These two event works fine separately but right now after tap and hold the click event fires to. How can I prevent the click event from firing after tap hold?
// Set the timeout
var holdTime = 500, timeout;
// Create the table touch start event listener
table.addEventListener('touchstart', function(e) {
// Set the selected user id
var itemValue = e.row.value_full;
// Define the function
timeout = setTimeout(function(e) {
// Create the fade out animation
var fadeOut = Titanium.UI.createAnimation({
curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT,
opacity: 0,
duration: 1000
});
// Create the loading screen
var copied = UI_messages.showFlash({label : 'Copied!'});
// Add the loading screen
win.add(copied);
// Save value to clipboard
Titanium.UI.Clipboard.setText(itemValue);
// Fade the message out
copied.animate(fadeOut);
}, holdTime);
});
// Create the event listener for touch move
table.addEventListener('touchmove', function() {
// Clear the timeout
clearTimeout(timeout);
});
// Create the event listener for touch move
table.addEventListener('touchend', function(e) {
// Clear the timeout
clearTimeout(timeout);
});
I've run into this problem before as well. The solution I used isn't very pretty, but it's the only effective way that I've found to suppress a touch event after a touch-and-hold.
The only working solution that I could find was to create a bool variable in a global namespace. In your setTimeout function, change the value of the bool to true to indicate that a touch-and-hold has occurred.
In the onClick event event for the row, check the global variable first to see if you've already created a touch-and-hold event - if you have, just return from the onClick event. This will effectively disable your click event when a touch-and-hold occurs.
Remember to set the global variable to false after the touch-and-hold function ends.