justintaddei / v-shared-element

Declarative shared-element transitions for Vue.js

Home Page:https://justintaddei.github.io/v-shared-element/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Brainstorming how to allow page "fade out"

719media opened this issue · comments

Trying to figure out if there is a way to "fade out" the content that won't be getting transitioned, but coming up empty. So just creating this issue to brainstorm any ideas.

An example is here:
https://codesandbox.io/s/v-shared-element-special-fade-hrh9o?file=/src/App.vue

The first router-view works fine (no transition)

The second router-view works fine
because adhering to requirements:
*using mode out-in
*don't do any duration on the "out")

The third router-view doesn't quite work right, because there is duration on the "out". The reason it does this makes total sense, but obviously the desired result is that the elements which will be "transitioned" would not fade out with the rest of the content.

From how I understand this library, the new "elements" are not drawn until the new component "inserts" them from the directive, so since this isn't happening until n seconds later (where n is the duration of the "out" on the router-view), it doesn't get drawn. Makes sense. I wonder if there's a way to draw it earlier somehow... doesn't seem like there is a straightforward solution...

You can get the desired behavior "sort of" by abandoning "out-in" mode, see example 4 here:

https://codesandbox.io/s/v-shared-element-special-fade-13hp5?file=/src/App.vue

The downside to this approach is that when you use the default mode (both), then scrolling looks like butt. You scroll down a bit on the main page, then click on something, now you've got an outgoing router-view that has scroll, and and incoming one that does not, and it isn't possible to really recover from what I can tell. At least with the mode="out-in", it looks nice even on scroll (example 2)

So dealing with this first because it's easier-ish

A very minor note, the "text" above the image is moving much faster (default 0.3s I believe),
so you do get a slight "flash" on that content from the router-view transition finishing,
which isn't desirable, but most likely this would not be animating at this speed, so it's not
really the objective here.

If we have some kind of "release" hook somehow, we could just wait until the page transition is complete before removing the clone of the "to"/"end" element. See Hooks to give you some ideas.

As far as the main issue, if we have some way of knowing when the animation needs to begin. Currently, I'm using vue-router's beforeEach to clone the "starting" element before the "ending" element is inserted. Using illusory's IllusoryElement.prototype._setParent(el) it would be possible. It would, however, require a (trivial) update to illusory because right now it assumes it isn't in the DOM until the main illusory function is called. That's easy to fix.

There is still one downside... it would still have to wait for the ending element to exist before it can start moving because otherwise, it wouldn't know where to go. But this approach does fix the flash (I think...).

You can get the desired behavior "sort of" by abandoning "out-in" mode, see example 4 here:

https://codesandbox.io/s/v-shared-element-special-fade-13hp5?file=/src/App.vue

The downside to this approach is that when you use the default mode (both), then scrolling looks like butt. You scroll down a bit on the main page, then click on something, now you've got an outgoing router-view that has scroll, and and incoming one that does not, and it isn't possible to really recover from what I can tell. At least with the mode="out-in", it looks nice even on scroll (example 2)

I've experimented with "in-out" in the past. If you set the page to position: absolute; while the animation takes place it sort of fixes the scrolling issue (but I think it brakes vue-router's scroll preserving feature. So not exactly a production ready fix).

illusory@1.4.0 adds IllusoryElement.prototype.attach which should let us do what we need.

Well... it almost works. Except now every shared element stays visible regardless of whether it has a matching element on the next page.

It's a step in the right direction at least.

Have a look: https://codesandbox.io/s/v-shared-element-with-page-transitions-0jkyr?file=/src/components/SharedElement/routeGuard.js

definitely more lag at the beginning, presumably from doing more work cloning?

Partially, yes. It's actually the same amount of cloning (although, each clone is now being appended to the DOM whereas before it wasn't). The longest part of the delay is actually because it has to wait for all the content to fade out before it can know where to move (the second page doesn't exist in the DOM until then).

If we can find a convenient way to mark one or more shared-elements as "enabled" and the rest as "disabled" it would work. Not sure what a good API would look like though... perhaps it would be better if it was just a generic way of disabling a given element? I think #7 would be another good use-case for this.

OK
I got it working with this:
https://codesandbox.io/s/v-shared-element-special-fade-kiit2?file=/src/views/Home.vue

Uses scrolling, fade in/out, and all works

Key points:

  • Using beforeLeave, afterLeave js hooks to take care of positioning
  • For remembering scroll position on previous screen: using scrollBehavior to remember the html history position. This is vue standard boilerplate. If you wanted to remember position for routes not using history (e.g. I am using router.go(-1), but if you just wanted it to work without that...), then could come up with another method. Point is, scroll position remembering is something you need to handle.

Wow. That works really well!

Looks more like something for a "common recipes" section in the docs though... I'm not really sure how this would be implemented as a feature given the need for CSS and <transition> hooks.

Ya I don't think it would be, either. I was just assuming that the answer would be something that involved v-shared-element more... but it didn't... at least for this use case. This example worked with:

  • keep-alive
  • scrolling
  • both fade out (leave) and fade in (enter)

I wasn't sure if we were going to be able to get it all working with all of the above, so I'm pleasantly surprised

As am I. Great work! Would you like to write a recipe for it? You probably have a better understanding of this one than I do. I created a new RECIPES.md file to keep all of these. If not, that's fine. I'll go ahead and write it for you (and list you as the author).

On second thought, I'm going to completely rewrite the documentation and just credit you in a section about page transitions.

Thank you for doing all the work. If you'd like to be listed as a contributor just let me know.

I really haven't done much, but if you'd like to list me as a contributor I'd gratefully accept. If there's something I can do to help write the documentation, please let me know.

So, I can't figure out how to add you as a contributor to the repo since you haven't made an actual PR yet. I did, however, credit you in the new document I just wrote. If you have a minute, would you mind combing over it for any errors or typos I might have missed? Of course, if you have any ideas to improve it, please let me know.

Thanks again. Just having someone to bounce a few ideas around with has done a lot for turning this project into something usable!