Pavan Kumar
Pavan Kumar

Reputation: 1911

Vue deferred transition between tow elements

Recently, I read a svelte tutorial on Deferred transitions. With few lines of code it was easy to achieve the transition as shown in GIF below.

I am curious on how to implement the same effect using Vue transitions. I read docs on Vue Transition and TransitionGroup, but I couldn't see how I could achieve same effect.

enter image description here

Upvotes: 1

Views: 116

Answers (1)

Pavan Kumar
Pavan Kumar

Reputation: 1911

Following is my attempt to acheive the described animation using vue 3.

<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'

const todos = ref([
  { title: 'Task 1', done: false },
  { title: 'Task 2', done: false },
  { title: 'Task 3', done: false },
  { title: 'Task 4', done: false },
  { title: 'Task 5', done: false }
])

const pendingList = computed(() => todos.value.filter((t) => !t.done))
const doneList = computed(() => todos.value.filter((t) => t.done))

const pendingListRef = ref<any[] | null>(null)
const doneListRef = ref<any[] | null>(null)

function pendingBeforeLeave(el: any) {
  nextTick(() => {
    const eleInDone = doneListRef.value?.find((e) => e.id === el.id)
    const translateX = eleInDone.offsetLeft - el.offsetLeft
    const translateY = eleInDone.offsetTop - el.offsetTop
    el.style.transform = `translate(${translateX}px, ${translateY}px)`
  })
}

function doneBeforeLeave(el: any) {
  nextTick(() => {
    const eleInPending = pendingListRef.value?.find((e) => e.id === el.id)
    const translateX = eleInPending.offsetLeft - el.offsetLeft
    const translateY = eleInPending.offsetTop - el.offsetTop
    el.style.transform = `translate(${translateX}px, ${translateY}px)`
  })
}
</script>

<template>
  <div class="todo-container">

    <div class="list">
      <h1>Todo</h1>
      <TransitionGroup @beforeLeave="pendingBeforeLeave" name="pending" tag="ul">
        <li
          class="list-item"
          :id="todo.title"
          ref="pendingListRef"
          v-for="todo in pendingList"
          :key="todo.title"
          @click="todo.done = true"
        >
          {{ todo.title }}
        </li>
      </TransitionGroup>
    </div>

    <div class="list">
      <h1>Done</h1>
      <TransitionGroup @beforeLeave="doneBeforeLeave" name="done" tag="ul">
        <li
          class="list-item"
          ref="doneListRef"
          :id="todo.title"
          v-for="todo in doneList"
          :key="todo.title"
          @click="todo.done = false"
        >
          {{ todo.title }}
        </li>
      </TransitionGroup>
    </div>
  </div>
</template>

<style scoped>
.list-item {
  transition-property: transform, opacity;
  transition-delay: 0s, 1.1s;
  transition-duration: 1s, 0.1s;
  transition-timing-function: ease, linear;
}

.pending-enter-from,
.done-enter-from {
  opacity: 0;
}
.pending-enter-to,
.done-enter-to {
  opacity: 1;
}

/* Below style defining look and feel nothing to do with animation*/

.todo-container {
  display: flex;
  flex-direction: row;
  justify-items: self-end;
  min-height: 100vh;
  padding: 15px;
  margin-left: auto;
  margin-right: auto;
  justify-content: center;
}

.list {
  width: 400px;
}

.list ul li {
  margin: 5px;
  padding: 15px;
  background: grey;
  list-style: none;
  cursor: pointer;
}
</style>

Here is working example: https://playcode.io/1342702

Upvotes: 0

Related Questions