admin管理员组

文章数量:1420279

I have a list of Profiles that open an "edit profile" screen. This screen slided in from the left. When I select a profile, if there is a screen already selected, I want it to slide out first, change the selected profile data and then slide in.

What happens now is: when I first select one element, the screen slides in. When I change the selected element, screen stays and don't slide out and back in.

Here is a gif to show how it's behaving now:

My code is:

Vue Method:

editProfile: function (index){
    // this.editingProfile = false;
    this.setProfile(index);
    this.editingProfile = true;

}

Html View:

        <transition name="fade" mode="out-in">
            <div v-if="editingProfile" id="edit-profile">
                <input placeholder="Profile Name" v-model="synced.profiles[synced.selectedProfile].name">
            </div>
        </transition>

CSS:

.fade-enter-active, .fade-leave-active {
   transition: all .2s;
   /* transform:translateX(0); */
  }
  .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
    opacity: 0;
    transform:translateX(-100%);
  }

How do I make it properly slide out and then back in when changing a profile?

I have a list of Profiles that open an "edit profile" screen. This screen slided in from the left. When I select a profile, if there is a screen already selected, I want it to slide out first, change the selected profile data and then slide in.

What happens now is: when I first select one element, the screen slides in. When I change the selected element, screen stays and don't slide out and back in.

Here is a gif to show how it's behaving now:

My code is:

Vue Method:

editProfile: function (index){
    // this.editingProfile = false;
    this.setProfile(index);
    this.editingProfile = true;

}

Html View:

        <transition name="fade" mode="out-in">
            <div v-if="editingProfile" id="edit-profile">
                <input placeholder="Profile Name" v-model="synced.profiles[synced.selectedProfile].name">
            </div>
        </transition>

CSS:

.fade-enter-active, .fade-leave-active {
   transition: all .2s;
   /* transform:translateX(0); */
  }
  .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
    opacity: 0;
    transform:translateX(-100%);
  }

How do I make it properly slide out and then back in when changing a profile?

Share Improve this question asked Aug 13, 2018 at 17:04 sigmaxfsigmaxf 8,51215 gold badges70 silver badges133 bronze badges 5
  • Are you properly toggling this.editingProfile true falsey – Helping hand Commented Aug 13, 2018 at 17:18
  • if I toggle it false and then true again, nothing happens. – sigmaxf Commented Aug 13, 2018 at 17:21
  • I think you'll need a set of panels as apposed to a single panel that you adjust the content of. Otherwise Vue is going to try to recycle that panel and ignore the transition. You want more of a list transition with item in-out animations. – zero298 Commented Aug 13, 2018 at 17:24
  • inside editProfile, you can set false first, then in this.$nextTick, set to true again inside setTimeout(()=>{}, 200), please check this fiddle. As @zero298 mentioned, group transition should be the solution for slide in/out. – Sphinx Commented Aug 13, 2018 at 17:27
  • You can try with single element i.e profile1 if true false toggle slide in & out is working or not. Also do you call editProfile when clicked one of the vertical list element – Helping hand Commented Aug 13, 2018 at 17:34
Add a ment  | 

2 Answers 2

Reset to default 4

I think I was incorrect with my ment. One way you can do this is to leverage :key and a v-if so that you can tell Vue to render a panel if you have selected one and then transition between panels that way. You won't need to have a transition-group then.

The thing is :key is what tells Vue that everything has changed. If you leave it off, Vue tries to recycle as much as it can. See the docs: Transitioning Between Elements

When toggling between elements that have the same tag name, you must tell Vue that they are distinct elements by giving them unique key attributes. Otherwise, Vue’s piler will only replace the content of the element for efficiency. Even when technically unnecessary though, it’s considered good practice to always key multiple items within a <transition> ponent.

Consider the minimal example below:

const panels = [{
    title: "Hello"
  },
  {
    title: "World"
  },
  {
    title: "Foo"
  },
  {
    title: "Bar"
  }
];

const app = new Vue({
  el: "#app",
  data() {
    return {
      panels,
      activePanel: null
    };
  },
  puted: {
    titles() {
      return this.panels.map(panel => panel.title);
    }
  },
  methods: {
    handleTitleClick(idx) {
      if (this.activePanel === idx) {
        this.activePanel = null;
        return;
      }
      this.activePanel = idx;
    }
  }
});
body {
  margin: 0;
  padding: 0;
}

#app {
  display: flex;
  align-items: stretch;
  height: 100vh;
}

#panel-set {
  flex: 1 0 70%;
  display: flex;
}

#side-panel {
  flex: 1 0 30%;
}

.panel {
  padding: 1em;
  flex: 1 0;
  background-color: rgba(0, 0, 0, 0.2);
}

.slide-fade-enter-active,
.slide-fade-leave-active {
  transition: transform 500ms ease-in-out;
}

.slide-fade-enter,
.slide-fade-leave-to {
  transform: translateX(-100%);
}
<script src="https://cdn.jsdelivr/npm/vue/dist/vue.min.js"></script>


<div id="app">
  <div id="panel-set">
    <transition name="slide-fade" mode="out-in">
      <div class="panel" v-if="activePanel !== null" :key="activePanel">
        <h2>{{panels[activePanel].title}}</h2>
        <p>Lorem ipsum</p>
      </div>
    </transition>
  </div>
  <div id="side-panel">
    <ul>
      <li v-for="(title, idx) in titles" @click="handleTitleClick(idx)">{{title}}</li>
    </ul>
  </div>
</div>

The root cause is v-if="editingProfile" always true after showing one profile in your codes.

One solution is set it to false first, then in this.$nextTick to set it to true again. But you have to put this.editingProfile = true inside one setTimeout and delay time = transition time. Otherwise, slide out effect will be overwritten.

Like below demo:

new Vue({
  el: '#emit-example-simple',
  data() {
    return {
      editingProfile: false,
      synced : {
        profiles: [{'name':'A'}, {'name':'B'}, {'name':'C'}],
        selectedProfile: 0
      },
    }
  },
  methods: {
    editProfile: function (index){
      this.editingProfile = !this.editingProfile
      this.$nextTick(() => {
        setTimeout(()=> {
        this.synced.selectedProfile = index
        this.editingProfile = true
        }, 1200)
      })
    }
  }
})
.fade-enter-active, .fade-leave-active {
   transition: all 1.2s;
   /* transform:translateX(0); */
  }
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
  transform:translateX(-100%);
  border: 1px solid white;
}
<script src="https://unpkg./vue/dist/vue.js"></script>

<div id="emit-example-simple">
  <button @click="editProfile(0)">Profile 1</button>
  <button @click="editProfile(1)">Profile 2</button>
  <button @click="editProfile(2)">Profile 3</button>
  <transition name="fade" mode="out-in">
      <div v-if="editingProfile" id="edit-profile">
          <input style="border: 5px solid red;" placeholder="Profile Name" v-model="synced.profiles[synced.selectedProfile].name">
      </div>
  </transition>
</div>

Or you can consider to use Group transition like below simple demo:

new Vue({
  el: '#emit-example-simple',
  data() {
    return {
      editingProfile: false,
      profileContainers: [true, false],
      synced : {
        profiles: [{'name':'A'}, {'name':'B'}, {'name':'C'}],
        selectedProfile: 0
      },
    }
  },
  methods: {
    editProfile: function (index){
      this.synced.selectedProfile = index
      this.profileContainers = this.profileContainers.map((x)=>!x)
    }
  }
})
.list-items-enter-active {
 transition: all 1.2s;
}
.list-items-leave-active {
 transition: all 1.2s;
}
.list-items-enter, .list-items-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
  transform:translateX(-100%);
  border: 1px solid white;
}

.list-item {
  display: inline-block;
  border: 6px solid red;
}
<script src="https://unpkg./vue/dist/vue.js"></script>

<div id="emit-example-simple">
  <button @click="editProfile(0)">Profile 1</button>
  <button @click="editProfile(1)">Profile 2</button>
  <button @click="editProfile(2)">Profile 3</button>
  <transition-group name="list-items" tag="p">
    <div v-for="(item, index) in profileContainers" :key="index" v-if="item">
        <input style="border: 5px solid red;" placeholder="Profile Name" v-model="synced.profiles[synced.selectedProfile].name">
    </div>
  </transition-group>
</div>

本文标签: javascriptVuejs transition on changing selected element from a listStack Overflow