framer-motion - dom node does not unmounts immediately after exit animation - javascript

I have created an animated modal container
it's working fine for the most part
the only issue is after doing the exit animation it takes a second or two to unmount (in that period cannot click anywhere else)
code sandbox - https://codesandbox.io/s/restless-platform-oovo1?file=/src/App.js
To see the issue 👇🏻
click show modal button
Click anywhere to close the modal
notice that the scrollbar still exist denoting modal is not unmounted
Also, you cannot reopen modal immediately on clicking of the open modal button after exit animation (for that 1 sec)
I had seen safeToremove but didn't understand how to use it as there is no proper documentation

I agree that the documentation is a little frustrating where this is concerned - here's what I've learned through playing around with it.
The documentation shows the code for usePresence() within the presence component to be:
import { usePresence } from "framer-motion"
export const Component = () => {
const [isPresent, safeToRemove] = usePresence()
useEffect(() => {
!isPresent && setTimeout(safeToRemove, 1000)
}, [isPresent])
return <div />
}
This initially seemed arbitrary/purely an example. However, after playing around with it, it turns out that if you initialize usePresence(), this useEffect code is now necessary for the presence to work correctly and in part determining the functionality of the presence. Try initializing usePresence and then commenting out the line
// !isPresent && setTimeout(safeToRemove, 1000)
It will break your animate presence.
So the setTimeout function wrapping safeToRemove is, in fact, determining core functionality of the AnimatePresence and able to determine the timing of the virtual DOM becoming clickable again, etc.
Now, I was playing around with the crossfade AnimateSharedLayout, so it was a little weird to be messing with the safeToRemove timing when I wasn't determining the animations themselves, but if you are setting up the animations for the modal yourself, I think that if you sync the time in that setTimeout function with the length of your animations, it should solve your problem.
I'm still trying to figure out exactly how all this works, so comment back and let me know what you find and we'll be one step closer to mastering using Framer v2's newest tools.

Related

Why result of state update is reacting in 2 different ways

I have created a simple TicTacToe using react-hooks.
Sometimes I am getting result before my state update is shown on screen.
Sometimes it is showing result after update is shown on screen.
I am not getting why it is reacting in 2 different ways.
Please find the code link below.
playground link
alert() function is blocking function so when alert window opens your app is on pause until user hit ok button.
alert function can sometimes race with rendering, that's why sometimes the alert shows before the last render and your app on pause so you will not see the change on the grid.
it happens when you
The solution is to replace alert() whith another visual element such as modals
Issue probably comes from your useState, you can try adding dependency array for the useState with player change so it only checks the game after each round.
From:
useEffect(() => {
CheckGame();
});
To:
useEffect(() => {
CheckGame();
}, [player]);

Infinite loop when overriding graphHandlerMouseUp on MxGraph using Angular

I have a difficult question to explain and I'm way out of my comfort zone as far as expertise in Javascript, TrueType, Angular and MxGraph are concerned... Hope to be able to explain.
I have an Angular component displaying and MxGraph. I was able to integrate MxGraph with Angular following this link (How to integrate mxGraph with Angular 4?). Even if I use Angular 7, the solution still works...
The graph is displayed correctly on the page and everything works fine, including my override of the function graphHandlerMouseUp, which I do with this code:
// Save the position of the mouse when releasing the button: used for
// detecting the target in a drag and drop
mx.graphHandlerMouseUp = mx.mxGraphHandler.prototype.mouseUp;
mx.mxGraphHandler.prototype.mouseUp = function( graph, evt ) {
currentdropX = evt.graphX;
currentdropY = evt.graphY;
mx.graphHandlerMouseUp.apply(this, arguments);
}
When I run this page for the first time, no problem happens.
Then through a button I call a page with another component (through routing). If from this page I go back to the first component (again through a routerlink) the page and the component with the MxGraph loads correctly, BUT when I use this function (i.e., release the mouse button).
It seems to me a recoursive problem, as when I put a console output like this:
// Save the position of the mouse when releasing the button: used for
// detecting the target in a drag and drop
mx.graphHandlerMouseUp = mx.mxGraphHandler.prototype.mouseUp;
mx.mxGraphHandler.prototype.mouseUp = function( graph, evt ) {
currentdropX = evt.graphX;
currentdropY = evt.graphY;
// INIFINTE LOOP HERE
console.log("Test");
mx.graphHandlerMouseUp.apply(this, arguments);
}
The "Test" is written a number of times which is continuously growing. Yet, if I understood, this was the correct way of overriding the function. Of course on the first load of the page, "Test" is displayed once. Passing to another component and then back on this it is displayed an "infinite" number of times (until I reach the: "ERROR RangeError: Maximum call stack size exceeded")...
I tried also to remove that override, and besides the obvious lack of the functionality, the very same problem happened to the function "mxDragSource", which is overridden with the same approach.
As I said: I'm not expert enough in javascript, truetype, MxGraph or Angular, so any hint, even if obvious, is very welcome!
Thanks!
The first time you run your code, you store mx library's mouseUp event in a variable and you assign a new function to mouseUp event in which you call the old mouseUpEvent.
The second time you run your code, you store your current mouseUp event (that you have modified) and you assign a function in which you call the old mouseUpEvent, which is the same you stored previously. There goes your recursion !
What you need to do is override the third-party function properly, so you don't execute your code twice.
How to do it ?
You can create a mixin and use this mixin in your componentB/ If you have no idea what is it and how to do it, please reproduce your problem in a stackblitz I'll be glad to help you to implement it.

Bootstrap v4 .modal("show") slow execution

I've noticed that bootstrap's modals take time to show as the page's content becomes more significant.
It takes less than 100ms to show when the page is empty, but takes proportionally more time as the amount of content in the page becomes more significant.
I do not understand this correlation. Can anybody explain this behavior and suggest a workaround to make the modals appearance faster independently of the page's size ?
EDIT: You can reproduce the behavior by going i.e here and execute the command
console.time("modalTime"); $("#exampleModal").modal("show"); console.timeEnd("modalTime");
in console, to see how much time it takes. Then add more content to the page by manipulating the DOM and re-execute the command.
My results: modalTime : 70 ms on the original page. modalTime : 1208 ms after making the content of the page 10x larger.
Ok, I am able to reproduce a delay if I add 50.000 lines of text to the document:
https://plnkr.co/edit/hvRAn3wg91GBCPxK2gwb?p=preview
The problem with a huge DOM like this is, that any manipulation will take long.
It might be a problem of jQuery, but I doubt.
To prove that the delay has nothing to do with the Modal, I am painting the button to red when clicking it - even this simple action takes as long as opening the modal window.
jQuery(document).ready(function() {
for (var i = 0; i < 50000; i++) {
$( "#content" ).append( "This is just some test. This is just some test. This is just some test. This is just some test. This is just some test. This is just some test. This is just some test. This is just some test. This is just some test. <br/>" );
}
$("#btnToggleMoadal").click(function(e) {
$("#exampleModal").modal("show");
$(this).css({backgroundColor:"red"});
});
});
Edit: I have also tested jQuery's onClick vs onClick out of the DOM - but there is no difference. https://plnkr.co/edit/483Sk2FGXk9lT8dLZIdo?p=info
Edit: I have to correct my answer. It's definitely the Bootstrap Modal which causes the performance issue. I think the problem is somewhere around _adjustDialog() in modal.js.
I recommend to open an issue at https://github.com/twbs/bootstrap/issues and show the Plunker example.

Slider animation via keyboard not working (javascript and CSS)

I am creating an slider in JS (with angular) + CSS where the user should control via keyboard (left and right arrows). I made it simple as you can see below.
My code works this way: When the user clicks right, it adds one object in the begining of a array and delete the last one. When I click left, it makes the opposite.
slideEffect = function(dir) {
if (dir == 'left') {
lastChamp = champs[champs.length-1];
champs.unshift(lastChamp);
champs.pop();
} else {
firstChamp = champs[0];
champs.push(firstChamp);
champs.shift();
}
}
The thing is. It works just fine when the user press "left arrow" but it goes wrong as soon as the user press right as you can see at: http://brunoornelas.com.br/test/pfc/
Does anyone have any clue on how to solve it the simplest way as possible and using Javascript? (I can't use jquery).
Thank you!
Your animation is working, it's just that the push from first to last messes the effect. Ideal way would be to wait till the transition ends before pushing the element. Since we don't really have access to your actual code, it's a bit hard to give a proper solution, ideal solution would be to add a transitionend listener and push then. But quickly, you can try this in your slide right function, you'll see what needs to be done:
firstChamp = champs[0];
champs.shift();
setTimeout(function(){champs.push(firstChamp)},100);

Delay a javascript function until a css animation is complete

I am trying to make a modal object library that will create and open a div, iframe, img, similar to colorbox. I am doing this in pure javascript, so please do not recommend jQuery.
The problem is, when a user creates a new modal using var myModal = new modal(options, width, height), I want it to check if a modal already exists, close it, wait for the close animation, then continue to create the new modal. I can already do everything, but I'm having an issue waiting to create the new modal until the old one is gone. I am aware of webkitTransisionEnd and firing custom events, but that is not the issue. I need the actual code to wait until the old modal is finished closing until it continues on to finish the rest of the function and still return the correct object to the user. Here are some of the things I've tried:
Creating a transisionEnd listener waiting for the animation to end then creating the new modal. (this worked but considering it then becomes a nested function, it's hard to return the correct object).
Using a try, catch block. (this didn't work for my purposes)
Using a countless number of variations of the same thing where I use recursive functions
If anyone has ideas, please feel free to post them. I have tried a lot of things, but apparently not the one thing that I need to. Thanks.
EDIT:
I was able to figure it out. All I had to do was attach a transitionEnd listener to the modal that is already open, then create an additional function outside of the class that would then recall the modal with the same constructor. The code looks a bit like this:
function create(options, width, height) {
return new modal(options, width, height);
}
function modal(options, width, height) {
if (modal != null) {
modal.close();
modal.addEventListener('webkitTransitionEnd', function() {
create(options,width,height);
});
}
return;
}
var animationDuration = 1000;
setTimeout(function(){
// Animation done!
}, animationDuration);
You can't cause code to wait (e.g. pause execution of the current thread of execution) until some future event occurs. Javascript simply does not support that or work that way. It does not have a way to block the current thread of execution other than a couple modal functions like alert().
What you can do is use callbacks to notify some calling code of a future event. But, the calling code will register its callback and be returned to immediately and continue executing so the calling code has to be written to handle the callback implementation.
If you're trying to do all the work inside your library, then it should not be that tough. When the caller creates a new modal, you just have to check for a pre-existing modal dialog. If one is not up you proceed as normal. If one is up, then you register a callback notification with the previous one, store the contents of the constructor, but don't actually create the new modal dialog. Then, when your callback gets called to indicate the previous modal dialog has completed, you finish putting up the new modal.
If these modal dialogs are all of your own creation, then you need to implement completion notification on them so that when they are closed, they can notify any listeners that they're done now. If they use an animation to close and you want to wait for the close notification until the animation is complete, then you can implement that also. If you're using CSS3 animations, then as you appear to already know, you can use the transtionEnd event to know when an animation is done or if you know the timing of the animation and you don't need to be ms precise, you can also just use a setTimeout() to know approx when the animation is complete.

Categories

Resources