-
Notifications
You must be signed in to change notification settings - Fork 121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flat render thoughts #188
Comments
Plan of action for Flat Rendering Here is the brief plan to implement flat rendering Current progess #503 Tasks accomplished:
Challenges to overcome
|
I was thinking about this, and I think we have no choice but to hide the element with CSS instead before unmounting it in order to have a smooth animation. This reminds me of graphics rendering where you render to a hidden back plane and then "flip" it instead of rendering directly to the front plane. |
@raineorshine my current plan for unmount animation was to pre-determine all the nodes that should not animate if they unmount on next re-render. Unmount Animation Cases:
In this example thoughts In this example when cursor changes from
In this example
1, 2 has been easily achieved but there was a problem implementing case 3. Since case 3 depends on new cursor instead of previous cursor we cannot pre-determine the animation output for unmounting nodes before hand. So we have to check after every re-render that if the new cursor is a RANKED_ROOT and if it is then we should dynamically stop all the unmount animation. The problem is that the Transition component uses functional rendering and on entering exiting state it doesn't get any updated variable from the HOC including the cursor variable. Hacky Solution: I used |
@raineorshine These are the only way we can change the cursor right ?
Please let me know if I am missing something! |
There are the movement actions that you listed, which also include We will need a general solution. It will not be viable to individually test the different actions that result in cursor changes. You can however diff the old cursor and new cursor and use that to determine how the cursor is moving. |
@raineorshine It is clear that we cannot implement animations by predetermining before hand like my previous implementation. Let's land on a proper plan that we both agree on before we invest more time on this task. I have a plan in mind. Instead of just creating flat array of visible thoughts, we create flat array of all the invisible thoughts starting from the root and stop at the deepest visible nodes. The advantages are :
But the real question is why do we render all the unnecessary nodes that are invisible? They can cause performance issue because they have expensive render cycle and may re-render on cursor change. Since the main focus of this task is performance optimization we can't afford that. To address this issue we don't actually render Please let me know what you think! |
This sounds like a hybrid of the old rendering and the desired flat rendering. That's not what we want. We want clean rendering pipeline that calculates offsets rather than using dummy elements to fill space.
Why render an empty div for each Thought, instead of calculating the top offset? The latter is cleaner and gives us more control. Keep in mind that the vertical space is meant to be intelligently collapsed in the new rendering approach. We have to maintain vertical position invariants without allowing a lot of empty space to build up like in the current app. |
The production app likely will have an animation for this transition. Perhaps a very fast wipe up.
Yes, that's right. And it may potentially be a different animation than others.
Yes, although I assume that would not be a special case, but would be subsumed under the rule that "cursor descendants should not animate on unmount."
Yes. This falls under the same rule as above, since all thoughts are descendants of the root.
All animations depend on the new cursor I believe.
It doesn't have to be after the re-render, just after the state has been updated with the new cursor. The unmount itself is not triggered until the state has changed, so presumably we have access to the new cursor at that point.
I don't think checking specifically for the root is correct, but I do think that we will need to access the state directly from the Transition component in order for it to know how to animate. |
Let me know if that is enough feedback for you to take the next step towards a plan, or if you need more from me! Thanks. |
Yes I agree. But with absolute positioning comes lot of challenges for calculating vertical offsets. It is easy to calculate offset if nodes always have constant height. But as you can see the attached examples . At such cases we cannot calculate proper vertical offset for node Instead of making all the visible thoughts absolutely positioned we can wrap them in a div and absolutely position the main wrapper with vertical offset based on total number of invisible thoughts above them. That way we can tackle problem of calculating vertical offsets within visible thoughts div and can leave empty space above. But with that we need a way to properly time change of top offset of wrapper with completion of unmounting animations. What do you think ? |
Not sure what I was thinking before... we should not be absolutely positioning the thoughts vertically. We can leave that to the DOM. We should just be absolutely positioning the horizontal position of the thoughts. The only except is that when the user navigates, thoughts will be unmounted and would abruptly change the vertical position. In order to keep this smooth, we will need to animate the vertical position back into place. I don't believe this will require per-thought positioning though. We will just need to know the vertical offset from the transition.
Thoughts that are not rendered should not take up any space, so I'm not sure I understand the motivation here. |
We do not want to maintain the top offset. This was stated clearly in the original issue and in a PR comment. The content should slide up as thoughts are unmounted. |
Ah. I just got it. I was in misconception that we wanted to maintain top offset as we kept going deeper. Sorry my bad. Things just got lot easier. Phew! |
Calculating x offsets only, correct? Also, can you explain why you need to diff the flatArray? I was thinking that you only need to know where the new cursor is to know what kind of unmount is happening.
What about the onExiting handler? You can access state directly from there when the component is unmounting.
Looks like it was again fixed #626 (pmndrs/react-spring#626) a year ago and is waiting for v9 to be released (pmndrs/react-spring#632). We can always add the branch as a dependencies until it gets released officially. |
When the user navigates, thoughts will be unmounted and would abruptly change the vertical position. To keep it smooth we need to animate the vertical position by the number of thoughts above that are unmounting. To do that we need to calculate all unmoutning nodes that are above new cursor. Also when thoughts are added above , we may want similar animation so that it looks smooth. So for this reason we need to find difference between previous and new flat array. |
There is a props that we can pass called |
That's awesome. I was really excited to use react-spring. I will try this specific branch and get back to you! |
Yes
Not just the number, but the heights, as they may vary.
Okay, I think that makes sense. |
I don't see |
I was addressing the |
Ah, I thought we were still talking about |
@raineorshine After thought is deleted above I animate using translateY to move whole div to move up smoothly. And once list animation is complete and thoughts are actually unmounted, we need to set translateY back to zero. But it's being quite tricky to time this because springs are not time based. You can see in the gif at the end it animates smoothly but then somewhere between unmounting and setting translateY back to zero timing goes off. I will get back to you with updates. |
@raineorshine Instead of using transformY to animate the wrapper div when thoughts are unmounting, I decided to animate the height of the unmounting nodes. It was tricky but it looks good. I have attached video link to show the latest update. There are still many things to adjust and reconsider. Let me know what you think about this. Also let's discuss and finalize how we want the all animations to look. Latest update https://www.loom.com/share/dae94eae5478485db50b6b24704a60bc |
Nice work! This looks great! Animating the vertical position is a big change, so we don't know yet if it will feel smooth or jarring for the user. I think we have to experience it directly to know. I can say that the video looks good, and doesn't strike me as jarring. The animation helps you follow the change, even when the thoughts are moving around a lot. Seeing it in action with the cursor will be the real test though. The other idea I thought of was to preserve the vertical position and adjust the body scroll to prevent lots of white-space from building up. I'm not sure if that could be done smoothly though. It wouldn't be animated, but the abrupt scroll jump might cause a render artifact. |
I think re-creating the existing app and doing basic animations to start with is fine. I will eventually hire an animator to design the production animations. We should address that in a separate issue. The important point is that the infrastructure is there to support different animations. Flat rendering will be much more flexible, since we can remove a parent without removing its descendants. |
Yes I agree! For now you can pull the draft PR to see latest progress in action in your local system. |
Absolutely! I will keep working to complete the infrastructure. |
@raineorshine I was looking for similarities between
I think we will need to reuse logic from |
I think that is a very good assessment. Let's continue with It's also possible that |
@raineorshine Just came across a |
Yes, though it's hard to speculate on before we have a concrete case where it's breaking for us. |
Instead of rendering thoughts hierarchically in the DOM, render them all at the same level.
e.g. Instead of this:
We want them rendered in the DOM like this:
Instead of visually hiding ancestors, do not render them at all.
The aim of this task is to eliminate empty space above and below deeply nested thoughts and to increase performance by not rendering ancestors hidden by the autofocus. It will also allow more complex navigation animations.
Extra care must be taken when the cursor moves to ensure that the visible thoughts remain in exactly the same position within the viewport without any visual choppiness. This includes any custom scroll position handling.
This is primarily a refactor. Visually the app should stay nearly identical.
The text was updated successfully, but these errors were encountered: