Skip to content

Set "boundaries" for transitions at blocks #1480

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

Closed
jacwright opened this issue May 17, 2018 · 3 comments
Closed

Set "boundaries" for transitions at blocks #1480

jacwright opened this issue May 17, 2018 · 3 comments
Milestone

Comments

@jacwright
Copy link
Contributor

Pulled from #1431 (comment) and opened here as a new issue for further discussion

Note: The following opinions are coming from a view that animations and transitions are most often used in web apps to call attention to changes in a page that is already loaded, and that the area of the page handling the change should be the one defining the transition. For example, the part that deals with changing the entire page in a SPA as you navigate might fade the entire page in (or might choose not to employ any transition), but elements on the page wouldn't fade themselves in unless their data was not loaded at the initial transition, in which case they might fade in after their portion of the data was loaded. Coordinating transitions throughout a page to run together should still be possible for advanced effects but should not be the default.


Addressing this comment #1431 (comment):

Allowing child transitions to run will give fine-grained control and allow more robust use-cases. However, the vast majority of the time this will not be used. I propose the default be childtransition:false and childtransition:true be used instead for those cases when you want the lower animations to run.

I believe transition boundaries should be at the block level with EachBlock, AwaitBlock and IfBlock (including else of course). I don't think it should be at Component because a component with a transition at the top level (not inside a block) would have the expectation that the transition is run when the component is placed within an if/each using the same rules that an HTML element would within that if/each (e.g. skipping the intro/outro when first being added/removed).

The default rule of thumb should be, transition is tied to the if/each nearest it. It should not run (unless explicitly told to do so with an override) when that if/each is first inserted into the DOM or when it is removed, only when the value for the if/each changes and elements are added/removed.

If you want transitions to run across these boundaries then you can use childtransition:true. This would be necessary even if e.g. the outer if didn't have a transition. E.g.

{#if foo}
  <div childtransition:true>
    {#if bar}
      <p transition:fade>
        this will fade in and out when `bar` is toggled, but will
        abruptly disappear when `foo` is toggled
      </p>
    {/if}
  </div>
{/if}

This allows the most expected behavior to be default and prevents confusion that would otherwise prevail when changes in an app (route changes and other large and small changes) cause animations to run unexpectedly.

I believe this is the correct behavior for the majority of web apps and that the directives for childtransition:true and skipintro:false and skipoutro:false will be rarely used and only in special circumstances.

@Rich-Harris
Copy link
Member

Thanks for making this case. I definitely see where you're coming from. My own personal experience has been that the lack of nested transitions is a nuisance that forces me to employ unnecessarily 'creative' solutions, so my predisposition is to think that they should be the default.

Of course, I haven't had as long to experience the reverse annoyance, since nested transitions have only been available a short while.

It feels like someone coming to Svelte might find it less frustrating to have to figure out how to turn nested transitions off rather than figure out how to turn them on. I say that because it might not be obvious to someone why their nested transitions aren't running, and my own reaction would probably be to call it a bug. But i don't really know! I would love to hear from other folks to see if we can reach a consensus on what the correct default should be.

@jacwright
Copy link
Contributor Author

jacwright commented May 24, 2018

Perhaps I'm completely wrong in my assumptions and haven't used nested transitions in the past because they weren't available. Though, as I think through my current app, I can't think of anywhere I would use it.

If we make it easily flippable (e.g. a compiler option or root option) and pick a default, then we could see if there is a preference one way or the other with real use.

If you feel the default should be childtransition:true I would be more than happy with that if I could set my default to false. If you feel confident the default should always be true and I am an outlier with my thinking then you can forgo the compiler/root option and I will just use childtransition:false where needed in my app.

@jacwright
Copy link
Contributor Author

Whether the default for child transitions is true or false, I would still like to make the case for having the boundaries for child transitions to be only be at the blocks. The if/each/await blocks are the place where the visibility is toggled on and off. I believe this is the correct behavior and will simplify working with transitions when building svelte apps.

jacwright added a commit that referenced this issue Aug 25, 2018
Makes transitions only run when the closest non-component block containing them has changed state.

Implements #1480.

### Details

Without the `containedTransitions` compiler option, all transitions are run whenever their element is added/remove from the dom. With `containedTransitions` the transitions are only run when the closest non-component block (e.g. an if-block or each-block) changes state. This means the transitions on the items of an each-block will not run when the each-block first appears on screen, or when it is removed, but they will run when items are added/removed from the each block. In addition, if a whole page fades in with a transition at the page level, the there will not be other transitions occuring inside the page.

The transition containers do not include components, since a component is a reusable container and not a state-driven block. Thus, a transition at the root of a component will run when that component is added to the DOM inside an if-block or each-block the same way an element with a transition inside either of those blocks is run.
jacwright added a commit that referenced this issue Aug 29, 2018
Makes transitions only run when the closest non-component block containing them has changed state.

Implements #1480.

### Details

Without the `containedTransitions` compiler option, all transitions are run whenever their element is added/remove from the dom. With `containedTransitions` the transitions are only run when the closest non-component block (e.g. an if-block or each-block) changes state. This means the transitions on the items of an each-block will not run when the each-block first appears on screen, or when it is removed, but they will run when items are added/removed from the each block. In addition, if a whole page fades in with a transition at the page level, the there will not be other transitions occuring inside the page.

The transition containers do not include components, since a component is a reusable container and not a state-driven block. Thus, a transition at the root of a component will run when that component is added to the DOM inside an if-block or each-block the same way an element with a transition inside either of those blocks is run.
jacwright added a commit that referenced this issue Aug 29, 2018
Makes transitions only run when the closest non-component block containing them has changed state.

Implements #1480.

Without the `containedTransitions` compiler option, all transitions are run whenever their element is added/remove from the dom. With `containedTransitions` the transitions are only run when the closest non-component block (e.g. an if-block or each-block) changes state. This means the transitions on the items of an each-block will not run when the each-block first appears on screen, or when it is removed, but they will run when items are added/removed from the each block. In addition, if a whole page fades in with a transition at the page level, the there will not be other transitions occuring inside the page.

The transition containers do not include components, since a component is a reusable container and not a state-driven block. Thus, a transition at the root of a component will run when that component is added to the DOM inside an if-block or each-block the same way an element with a transition inside either of those blocks is run.

Intro and outro transitions will run unless `intro` or `outro` on the transition object (e.g. next to duration/delay) are set to false. Transition defaults can be set in the root options, including intro and outro.
jacwright added a commit that referenced this issue Aug 29, 2018
Allows transitions to skip running when they are added to the DOM or removed from it. Similar to the compiler option `skipIntroByDefault` this allows the intro/outro to be skipped.

Example:

```html
{#if visible}
  {#each things as thing}
    <div transition:fade></div>
  {/each}
{/if}

<script>
export default {
  transitions: {
    fade(node, params) {
      return {
        intro: false,
        outro: false,
        duration: 400,
        css: t => {
          return `opacity: ${t}`;
        }
      };
    }
  }
};
</script>
```

In this example items in the each-block will fade in and fade out when they are added to or removed from `things` but the list will appear suddenly and disappear suddenly (no fade) when `visible` is toggled to `true` or `false`.

This is another try at #1480 from the compiler option in #1692.

Intro and outro transitions will run unless `intro` or `outro` on the transition object (e.g. next to duration/delay) are set to false. Transition defaults can be set in the root options, including intro and outro.
jacwright added a commit that referenced this issue Sep 12, 2018
Allows transitions to only run locally, skipping running when they are added to the DOM or removed from it.

Example:

```html
{#if visible}
{#each things as thing}
<div transition:fade|local></div>
{/each}
{/if}

<script>
export default {
  transitions: {
    fade(node, params) {
      return {
        duration: 400,
        css: t => {
          return `opacity: ${t}`;
        }
      };
    }
  }
};
</script>
```

In this example items in the each-block will fade in and fade out when they are added to or removed from `things` but the list will appear suddenly and disappear suddenly (no fade) when `visible` is toggled to `true` or `false`.

This is another try at #1480.
Rich-Harris added a commit that referenced this issue Sep 26, 2018
@Rich-Harris Rich-Harris added this to the 3.x milestone Jan 19, 2019
Rich-Harris added a commit that referenced this issue Jan 27, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants