VueJS仅JS过渡挂钩需要setTimeout才能使CSS过渡正常工作

时间:2019-03-20 08:42:40

标签: javascript css vue.js css-transitions transition

我正在为VueJS上的<transition-group>元素使用纯JS钩子,而对于enter钩子实际上是如何工作的,我感到很困惑。根据文档,我知道我将必须call done() to avoid events being called synchronously

  

使用仅JavaScript转换时,doneenter钩子需要leave回调。否则,挂钩将被同步调用,并且过渡将立即完成。

但是,即使我使用它,也似乎阻止了CSS过渡在输入过渡中发生。我发现的唯一解决方案是使用window.setTimeout设置样式,我认为这是代码的味道。这是不带超时的代码与带超时的代码之间的快速直观比较(带超时的代码是理想的效果):

输入中断(左填充和不透明度没有转换)

Broken enter transition

所需的输入过渡:

Desired effect

在下面的示例中,我正在使用<transition-group>显示一个列表,并希望使用JS挂钩,以便可以在单个列表项上创建交错的填充。在enter过渡中,padding属性上的CSS过渡不起作用。

new Vue({
  el: '#app',
  data: {
    items: [
      'Lorem',
      'Ipsum',
      'Dolor',
      'Sit',
      'Amet'
    ],
    toggle: false
  },
  computed: {
    filteredItems: function() {
      if (!this.toggle)
        return [];

      return this.items;
    }
  },
  methods: {
    toggleItems: function() {
      this.toggle = !this.toggle;
    },
    beforeEnter: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    },
    enter: function(el, done) {
      el.style.paddingLeft = `${10 * +el.dataset.index}px`;
      el.style.opacity = '1';
      done();
    },
    beforeLeave: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    }
  }
})
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  transition: all 500ms ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">

  <button @click="toggleItems">
    Toggle items
  </button>
  
  <transition-group
    tag="ul"
    @before-enter="beforeEnter"
    @enter="enter"
    @before-leave="beforeLeave">
    
    <li
      v-for="(item, i) in filteredItems"
      v-bind:key="i"
      v-bind:data-index="i">
      {{ item }}
    </li>
  </transition-group>
</div>

如果您将enter方法中的所有逻辑都包装在任意timout中,那么它将起作用:

enter: function(el, done) {
  window.setTimeout(() => {
    el.style.paddingLeft = `${10 * +el.dataset.index}px`;
    el.style.opacity = '1';
    done();
  }, 100);
},

这是让我有些困惑的地方:enter挂钩是否不等待beforeEnter首先完成?工作片段如下

1 个答案:

答案 0 :(得分:2)

@enter钩子更改为@after-enter应该可以解决

我不知道为什么@enter钩对此不起作用,因为它在应该的文档中进行了说明,但这至少应该摆脱超时而不会成为黑客。

new Vue({
  el: '#app',
  data: {
    items: [
      'Lorem',
      'Ipsum',
      'Dolor',
      'Sit',
      'Amet'
    ],
    toggle: false
  },
  computed: {
    filteredItems: function() {
      if (!this.toggle)
        return [];

      return this.items;
    }
  },
  methods: {
    toggleItems: function() {
      this.toggle = !this.toggle;
    },
    beforeEnter: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    },
    afterEnter: function(el) {
      el.style.paddingLeft = `${10 * +el.dataset.index}px`;
      el.style.opacity = '1';
    },
    beforeLeave: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    }
  }
})
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  transition: all 500ms ease-in-out;
}

li.v-enter-active {
  transition: none
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">

  <button @click="toggleItems">
    Toggle items
  </button>

  <transition-group 
    tag="ul" 
    @before-enter="beforeEnter"
    @after-enter="afterEnter" 
    @before-leave="beforeLeave">
    <li v-for="(item, i) in filteredItems" v-bind:key="i" v-bind:data-index="i">
      {{ item }}
    </li>
  </transition-group>
</div>

请注意,如果您使用的是SCSS或SASS,则可以使用JavaScript而不是JavaScript来实现此目的