简体   繁体   English

Vue.js 转换从列表中更改选定元素

[英]Vue.js transition on changing selected element from a list

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:这是一个 gif,显示它现在的行为:

在此处输入图像描述

My code is:我的代码是:

Vue Method: Vue方法:

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

}

Html View: html视图:

        <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: 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 think I was incorrect with my comment.我认为我的评论不正确。 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.你可以做到这一点的一种方法是利用:keyv-if ,这样如果你选择了一个面板,你就可以告诉 Vue 渲染一个面板,然后以这种方式在面板之间进行转换。 You won't need to have a transition-group then.那时你不需要有一个transition-group

The thing is :key is what tells Vue that everything has changed.问题是:key告诉 Vue 一切都变了。 If you leave it off, Vue tries to recycle as much as it can.如果您不使用它,Vue 会尝试尽可能多地回收。 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.在具有相同标签名称的元素之间切换时,您必须通过赋予它们唯一的key属性来告诉 Vue 它们是不同的元素。 Otherwise, Vue's compiler will only replace the content of the element for efficiency.否则,Vue 的编译器为了效率只会替换元素的内容。 Even when technically unnecessary though, it's considered good practice to always key multiple items within a <transition> component .即使在技术上没有必要,<transition>组件中始终键入多个项目也被认为是一种好的做法

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 }; }, computed: { 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.net/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.根本原因是v-if="editingProfile"在您的代码中显示一个配置文件后始终为真。

One solution is set it to false first, then in this.$nextTick to set it to true again.一种解决方案是先将其设置为 false,然后在this.$nextTick中再次将其设置为 true。 But you have to put this.editingProfile = true inside one setTimeout and delay time = transition time.但是您必须将this.editingProfile = true放在一个setTimeout和延迟时间 = 转换时间中。 Otherwise, slide out effect will be overwritten.否则, slide out效果将被覆盖。

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.com/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:或者您可以考虑使用如下简单演示的Group transition

 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.com/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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM