اضافه‌کردن انیمیشن به لیست ساخته‌شده

Adding the Animation to the Created List

14 آبان 1399
Vue.JS 2: اضافه کردن انیمیشن به لیست ساخته شده - قسمت 67

در قسمت قبل لیستی ایجاد کردیم که دارای دکمه ای برای اضافه کردن عناصر جدید به آن بود و همچنین با کلیک روی هر کدام از اعضای این لیست، می توانستیم آن را از بین ببریم. مسئله اینجاست که عملیات حذف و اضافه کردن آیتم ها فعلا هیچ انیمیشنی ندارد و ما می خواهیم انیمیشن های دلخواه خود را به آن اضافه کنیم. یادتان باشد که نمی توانیم مثل همیشه از <transition> استفاده کنیم چرا که در <transition> همیشه باید یک عنصر نمایش داده شود اما اینجا لیستی از عناصر را داریم (چندین و چند <li>). بنابراین راه حل ما استفاده از <transition-group> است اما آیا استفاده از آن مانند <transition> می باشد؟ خوشبختانه پاسخ این سوال مثبت است؛ چه از کلاس های CSS و چه از hook های جاوا اسکریپتی برای اضافه کردن انیمیشن استفاده کنید، روند دقیقا یکی است.

تنها تفاوتی که بین <transition> و <transition-group> وجود دارد، این است که <transition> چیزی را به DOM اضافه نمی کند اما <transition-group> یک تگ <span> را به DOM اضافه خواهد کرد. ما نمی توانیم جلوی این مسئله را بگیریم اما می توانیم نوع تگ را به شکل زیر عوض کنیم:

<transition-group tag="YourTag">

به جای YourTag باید تگ مورد نظر خود را بنویسید. مثلا اگر بخواهیم به جای <span> یک تگ <p> اضافه شود می گوییم:

<transition-group tag="p">

برای شروع این جسله به فایل App.vue رفته و از <transition-group> استفاده می کنیم:

<ul class="list-group">
  <transition-group name="slide">
    <li
      class="list-group-item"
      v-for="(number, index) in numbers"
      @click="removeItem(index)"
      style="cursor: pointer"
    >{{ number }}</li>
  </transition-group>
</ul>

توجه کنید که خود <ul> درون <transition-group> قرار ندارد. من می خواهم از انیمیشن slide استفاده کنم (قبلا آن را تعریف کرده ایم) بنابراین مثل همیشه name را با مقدار slide به آن می دهم. حالا سوال این است که آیا کد بالا بدون مشکل کار می کند؟ پاسخ منفی است! اگر کدها را ذخیره کرده و در مرورگر باز کنید، کل لیست از بین می رود و دیگر نمایش داده نمی شود.

مشکل همان مشکلی است که قبلا با آن روبرو شده بودیم. اگر یادتان باشد در جلسات قبل هنگامی که با کادر های info و warning کار می کردیم، هر دو div را درون یک <transition> قرار دادیم تا یکی را مخفی کرده و دیگری را نمایش بدهیم:

<transition :name="alertAnimation" mode="out-in">
  <div class="alert alert-info" v-if="show" key="info">This is some Info</div>
  <div class="alert alert-warning" v-else key="warning">This is some Warning</div>
</transition>

اینجا هم همین مشکل را داشتیم و هر دو div کاملا مخفی می شدند. من در آن قسمت توضیح دادم که Vue توانایی تشخیص تفاوت بین این دو div را ندارد و اگر دو یا چند عنصر یکسان (مثلا چند div یا چند <li> یا هر عنصر دیگری) را به <transition> بدهیم، باید برایشان key تعریف کنیم تا Vue بفهمد که این عناصر، عناصر جداگانه ای هستند. البته این توضیحات مربوط به <transition> بود و در <transition-group> باید همیشه برای تمام عناصر key تعریف کنیم حتی اگر یکسان نباشند. بنابراین می گوییم:

<ul class="list-group">
  <transition-group name="slide">
    <li
      class="list-group-item"
      v-for="(number, index) in numbers"
      @click="removeItem(index)"
      style="cursor: pointer"
      :key="number"
    >{{ number }}</li>
  </transition-group>
</ul>

اگر یادتان باشد در جلسه قبل کدها را طوری نوشتیم که هیچ عدد تکراری درون آرایه numbers نداشته باشیم (همیشه یک واحد به طول آرایه اضافه می کردیم و آن را به عنوان عنصر جدید تحویل لیست می دادیم) بنابراین خود number بهترین مقدار برای key است چرا که یکتا است و هیچ وقت تکرار نمی شود. همچنین یادتان باشد که علامت دو نقطه (مخفف v-bind) را بگذارید تا رشته ساده number را به آن پاس ندهید بلکه مقدار پویای number (به عنوان یک متغیر نه یک رشته) را به آن بدهیم. اگر کدهای بالا را ذخیره کرده و به مرورگر بروید، تمام کدها بدون مشکل کار می کنند و حالا یک انیمیشن جالب داریم. تنها مشکل اینجاست که در این انیمیشن شاهد یک جهش در هنگام اضافه کردن یا حذف آیتم های لیست هستیم. یعنی زمانی که روی دکمه کلیک می کنیم تا عنصری به لیست اضافه شود، ابتدا عناصر جهش می کنند تا برای عنصر ما جا باز کنند و سپس آن عنصر به آرامی وارد لیست می شود.

خوشبختانه <transition-group> یک کلاس اضافی نسبت به <transition> دارد که مسئول همین کار است. ما می توانیم به قسمت style در همان فایل app.vue رفته و به دنبال کلاس های slide بگردیم:

.slide-enter {
  opacity: 0;
  /*transform: translateY(20px);*/
}

.slide-enter-active {
  animation: slide-in 1s ease-out forwards;
  transition: opacity 0.5s;
}

.slide-leave {
}

.slide-leave-active {
  animation: slide-out 1s ease-out forwards;
  transition: opacity 1s;
  opacity: 0;
}

حتما به خاطر دارید که تمام این کلاس ها را در جلسات اول این فصل تعریف کرده بودیم. کلاس اضافی ما slide-move است و می توانیم آن را به شکل زیر اضافه کنیم:

.slide-leave-active {
  animation: slide-out 1s ease-out forwards;
  transition: opacity 1s;
  opacity: 0;
}

.slide-move {
  transition: transform 1s;
}

خصوصیت transform در اینجا ربطی به transform استفاده شده در keyframe ها ندارد، بلکه Vue همیشه از این خصوصیت برای جا به جا کردن عناصر استفاده می کند (چه translateX و چه translate بر اساس نیاز استفاده خواهد شد). از آنجایی که Vue از transform برای حرکت دادن عناصر استفاده می کند، ما می توانیم به آن transition استفاده کنیم تا عملیات به نرمی و با انیمیشن صورت بگیرد. بنابراین در این کد به CSS گفته ایم که اگر خصوصیت transform تغییر کرد به آن انیمیشن اضافه کن (زمانی که Vue عنصری را جا به جا کند مقادیر translateX یا translate تغییر خواهند کرد بنابراین قابل تشخیص خواهند بود). در ضمن اگر قرار به استفاده از انیمیشن دیگری بود باید عینا همین مراحل را بروید. مثلا اگر بخواهیم به جای slide از fade (یا هر نامی که خودتان انتخاب کرده اید) استفاده کنیم، مراحل تغییری نخواهند کرد.

حالا اگر کدها را ذخیره کرده و به مرورگر برویم، در هنگام کلیک روی دکمه Add Item یک آیتم جدید با انیمیشن کاملا مطلوب اضافه خواهد شد بنابراین کد ما کار می کند اما اگر روی یکی از این آیتم ها کلیک کنیم تا حذف شود، باز هم شاهد آن جهش خواهیم بود! چرا؟ به دلیل اینکه آیتم در حال حذف در زیر یا بالای خود آیتم های دیگری دارد. این آیتم ها جزئی از document flow هستند (همان ساختار HTML صفحه و نحوه روی هم نشستن عناصر) و این Flow تغییر نمی کند مگر آنکه عنصری حذف شود و جای خالی برای بقیه عناصر به وجود بیاورد. مثلا امکان ندارد که آیتم های یک لیست به صورت خودکار جا به جا شوند یا تصویری در صفحه HTML به قسمت پایین تری برود مگر آنکه جای خالی پیدا شود. با این حساب اضافه کردن انیمیشن به آن ها هیچ فایده ای ندارد چرا TranslateY (حرکت عمودی) در حالت عادی اجازه نمی دهد عناصر روی هم بروند مگر اینکه position روی absolute باشد. بنابراین به کلاس slide-leave-active می رویم (این کلاس به عنصر در حال حذف اضافه می شود) و می گوییم:

.slide-leave-active {
  animation: slide-out 1s ease-out forwards;
  transition: opacity 1s;
  opacity: 0;
  position: absolute;
}

همانطور که می دانید، position: absolute به عناصر HTML اجازه می دهد که روی هم بروند. حالا اگر کدها را ذخیره کرده و به مرورگر بروید، همه چیز به درستی دارای انیمیشن است. دانلود سورس کد این قسمت.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش رایگان Vue js از صفر تا صد توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.