Динамический стиль для перехода в Vue

#javascript #css #vue.js

#javascript #css #vue.js

Вопрос:

Я работаю над компонентом боковой панели VueJS. Это должно позволять родительскому устройству указывать ширину и отображать переключатель, который перемещает боковую панель внутрь и наружу. Что-то вроде этого:

 <template>
  <div class="sidebarContainer">
    <transition
        name="slide"
    >
      <div v-if="isOpen" class="sidebar" :style="{ width }">
        <slot/>
      </div>
    </transition>
    <div class="toggle" @click="isOpen = !isOpen">amp;<amp;></div>
  </div>
</template>
  
 export default {
  props: {
    'width': {
      default: '20em',
    }
  },
  data() {
    return {
      isOpen: true,
    };
  },
};
  
 <style scoped>
.slide-enter-active, .slide-leave-active {
  transition: all 0.6s;
}
.slide-enter, .slide-leave-to {
  margin-left: -20em;
}
</style>
  

Работает codepen. Это работает именно так, как я хочу, за исключением того, что ширина перехода (как указано в .slide-enter, .slide-leave-to style) жестко запрограммирована и не реагирует на width свойство компонента. Если вы установите width=30em , то переход будет скачкообразным.

Я подозреваю, что мне, возможно, потребуется использовать перехваты перехода, но, похоже, я не могу заставить это работать. Я пробовал это:

 beforeEnter(el) {
  el.style = {
    transition: 'all 0.6s',
    marginLeft: '-20em',
  };
},
enter(el, done) {
  el.style.marginLeft = '0';
  done();
},
beforeLeave(el) {
  el.style = {
    transition: 'all 0.6s',
    marginLeft: '0',
  };
},
leave(el, done) {
  el.style.marginLeft = '-20em';
  done();
},
  

Смотрите измененный codepen. Вы можете видеть, что боковая панель все еще перемещается, но мгновенно, без анимации. Я подумал, что, возможно, перенос done обратного вызова в setTimeout , чтобы завершить переход, помог бы, но это не так.

Я знаю, что мог бы использовать библиотеку, подобную Velocity, или я мог бы вручную закодировать анимацию, но, похоже, должен быть способ просто позволить CSS позаботиться об этом. Чего мне не хватает?

Ответ №1:

Во-первых, ваш способ задать стиль просто не работает

 el.style = {
   transition: 'all 0.6s',
   marginLeft: '-20em',
};
  

Я просто перехожу transition: 'all 0.6s' к css и устанавливаю стиль следующим образом

 el.style.marginLeft = '-20em';
  

Во-вторых, enter событие вызывается очень скоро после beforeEnter события, поэтому браузер не может обнаружить изменение между двумя состояниями. Итак, я оборачиваю enter событие в setTimeout , чтобы сделать трюк для запуска перехода.

В-третьих, done обратный вызов в этом случае не требуется. Это требуется только в чистом переходе на js. Мы используем смесь CSS и JS

 Vue.component('app', {
  template: `<div class="app">
    <sidebar>sidebar content</sidebar>
    <div class="main">Hello, VueJS!</div>
  </div>`
});

Vue.component('sidebar', {
  template: `  <div class="sidebarContainer">
    <transition
        name="slide"
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave"
    >
      <div v-if="isOpen" class="sidebar" :style="{ width }">
        <slot/>
      </div>
    </transition>
    <div class="toggle" @click="isOpen = !isOpen">amp;<amp;></div>
  </div>`,
  props: {
    'width': {
      default: '20em',
    }
  },
  data() {
    return {
      isOpen: true,
    };
  },
  methods: {
    beforeEnter(el) {
      el.style.marginLeft = '-20em';
    },
    enter(el, done) {
      // Wait a tick here, so browser can detect style change and tigger transition
      setTimeout(() => {
        el.style.marginLeft = '0';
      }, 0)
    },
    leave(el, done) {
      el.style.marginLeft = '-20em';
    },
  },
});

new Vue({
  el: '#app',
  template: '<app/>'
});  
 html,
body,
.app {
  height: 100%;
}

.app {
  display: flex;
}

.main {
  flex-grow: 1;
  background: red;
}

.sidebarContainer {
  display: flex;
}

.sidebar {
  flex-grow: 1;
  padding: 0.5em;
  background: blue;
  transition: all 0.6s;
}

.toggle {
  margin: 0.5em;
}  
 <div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>  

Комментарии:

1. Я обнаружил, что мне пришлось установить время ожидания равным 1 вместо 0, но тогда это сработало идеально. Спасибо за всю вашу помощь!