Search code examples
vuejs3vue-transitions

Vue3 - delayed transition


I am trying to develop an animation which will sequentially load into the view once the side bar is toggled on

The animation should be similar to this one: https://codepen.io/dizzyluo/pen/yJLwWm

Here is my code:

Parent:

<template>
  <div>
    <button @click="cartToggle">Toggle</button>
  </div>
  <div>
    <Child :visible="cartVisible" @update:visible="cartToggle" />
  </div>
</template>
<script setup>
import { ref } from 'vue'
import Child from './Comp.vue'

const cartVisible = ref(false)
const cartToggle = () => {
  cartVisible.value = !cartVisible.value
}
</script>
<style scoped>
span {
  cursor: pointer;
}
</style>

Child:

<script setup lang="ts">
import { toRefs, watchEffect, onBeforeUnmount } from 'vue'
import CartItem from './CartItem.vue'

const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
    default: false
  }
})

const { visible } = toRefs(props)

const emit = defineEmits(['update:visible'])

const hideCartBar = () => {
  emit('update:visible', false)
}

watchEffect(() => {
  document.body.style.overflow = visible.value ? 'hidden' : 'auto'
  document.body.style.paddingRight = visible.value ? '15px' : '0px'
})

onBeforeUnmount(() => {
  document.body.style.overflow = 'auto'
  document.body.style.paddingRight = '0px'
})
const show = true
</script>

<template>
  <transition name="fade">
    <div
      v-if="props.visible"
      class="cartbar-overlay"
      @click="hideCartBar"
    ></div>
  </transition>
  <transition name="slide">
    <div v-show="props.visible" class="cartbar-container">
      <div class="cartbar__header">
        <h2>Menu</h2>
        <span @click="hideCartBar" class="cursor-pointer">X</span>
      </div>
      <div class="cart-items">
        <CartItem v-for="index in 3" :key="index" :delay="index" />
      </div>
    </div>
  </transition>
</template>

<style scoped>
/* transition */
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

.fade-enter-to,
.fade-leave-from {
  opacity: 1;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 500ms;
}

.slide-enter-from,
.slide-leave-to {
  transform: translateX(100%);
}

.slide-enter-to,
.slide-leave-from {
  transform: translateX(0);
}

.slide-enter-active,
.slide-leave-active {
  transition: transform 500ms;
}
/* cartbar */
.cursor-pointer {
  cursor: pointer;
}

.cartbar-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.7);
  cursor: pointer;
}
.cartbar-container {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  max-width: 460px;
  height: 100%;
  overflow: hidden;
  margin-left: auto;
  padding: calc(4px * 7);
  background-color: #f4f2f1;
  color: #706b5f;
}

.cartbar__header {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}
</style>

Delayed animated component:

<script setup>
import { toRefs } from 'vue'

const props = defineProps({
  delay: Number
})

const { delay } = toRefs(props)
</script>

<template>
  <transition appear name="slide">
    <div class="item">
      {{ delay }}
      <div class="image">
        <img src="https://placehold.co/75x100" />
      </div>
      <div class="meta">
        <h3>Lorem Ipsum</h3>
        <div class="meta-info">
          <p>Dolor sit amet dorum forum</p>
        </div>
      </div>
    </div>
  </transition>
</template>

<style scoped>
.slide-leave-active,
.slide-enter-active {
  transition: 1s;
  transition-delay: 0.25s;
}
.slide-enter {
  transform: translate(100%, 0);
}
.slide-leave-to {
  transform: translate(-100%, 0);
}

.meta-info {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  margin: 0;
  padding: 0;
}
.item {
  display: flex;
  flex-direction: row;
  margin: 10px 0;
}

.image {
  margin-right: 15px;
  height: 100%;
}

.image img {
  height: 100%;
}

.meta-info {
  font-size: 0.8rem;
}
</style>

Any idea what am I missing there?

Playground Link


Solution

    1. You need add v-show or v-if on <div class="item"> if you want the transition working correctly.
    2. You need rename the transition in components CartItem because the parent has a same name slide,and it affects the child elements.
    3. The props appear is usually used in a element that showing at first but not like this case,so I implement this function by add a ref isFirst.

    check the Playground Link