Dari produk ke keranjang: Tambahkan animasi terpandu ke pengalaman berbelanja

 – Beragampengetahuan
17 mins read

Dari produk ke keranjang: Tambahkan animasi terpandu ke pengalaman berbelanja – Beragampengetahuan

Halo! Saya Andrea Biason, seorang pengembang front-end kreatif di Adoratorio Studio dengan minat terhadap bola voli, kode, dan segala sesuatu yang dinamis (termasuk GIF!).

Dalam artikel ini, kita akan mempelajari cara membuat laman landas e-niaga sederhana dan mengubahnya menjadi pengalaman yang lebih interaktif dan menarik bagi pengguna, dengan tujuan akhir untuk meningkatkan konversi sekaligus membuat perjalanan pengguna lebih menarik pada saat yang sangat penting ini. .daya tarik. , namun sering kali diabaikan.

“Saya punya teman yang membutuhkan halaman arahan untuk produknya.

Ketika saya mendapat undangan untuk proyek ini, pikiran pertama saya adalah saya tidak ingin ini menjadi situs e-commerce biasa.
Jadi saya bertanya kepada desainernya: “Berapa banyak kebebasan berkreasi yang saya miliki?“.
Untungnya, jawabannya adalah “lakukan apa yang kamu mau”, jadi saya mulai memikirkan apa yang bisa saya lakukan agar hasilnya lebih menarik.

“Bagaimana jika kita menambahkan animasi saat tombol CTA diklik? Ikon keranjang belanja mungkin muncul…”

Sebenarnya…tidak! Interaksi pada tombol “tambahkan ke troli” adalah solusi yang tepat, tetapi saya tidak ingin menggunakan sesuatu yang telah saya lihat jutaan kali – saya ingin mencoba membuat sesuatu yang unik. Idenya muncul dari pemikiran tentang dua komponen yang benar-benar terpisah dan tidak berhubungan: galeri dan jalur mouse pada kursor. Saya pikir mungkin menyenangkan untuk mencoba menggabungkannya, menggunakan banyak gambar yang tersedia bagi kita untuk membuat Jalur dari produk ke keranjang.

Jenis interaksi ini tidak hanya melibatkan pengguna secara visual, tetapi juga mengarahkan pandangan mereka terhadap proses pembayaran dan proses pembayaran.

Mari kita lihat lebih dekat kodenya.

Contents

tanda

<section class="content">
  <div class="products">
    <ul class="products__list">
      <li class="products__item" data-id="product-01" data-price="15" data-name="Product 01" data-cover="/images/product-01-cover.jpg">
        <div class="products__images">
          <img class="products__main-image" src="/images/product-01-cover.jpg" alt="Product 01">
          <div class="products__gallery">
            <img class="products__gallery-item" src="/images/galleries/product-01/01.jpg" alt="Product 01 gallery">
            <img class="products__gallery-item" src="/images/galleries/product-01/02.jpg" alt="Product 01 gallery">
            <img class="products__gallery-item" src="/images/galleries/product-01/03.jpg" alt="Product 01 gallery">
            <img class="products__gallery-item" src="/images/galleries/product-01/04.jpg" alt="Product 01 gallery">
            <img class="products__gallery-item" src="/images/galleries/product-01/05.jpg" alt="Product 01 gallery">
            <img class="products__gallery-item" src="/images/product-01-cover.jpg" alt="Product 01 gallery">
          </div>
        </div>
        <button type="button" class="products__cta button">Add to cart</button>
      </li>
      <li>... </li>
      <li>... </li>
      <li>... </li>
      <li>... </li>
      <li>... </li>
    </ul>
  </div>
</section>

<aside class="cart">
  <div class="cart__bg"></div>
  <div class="cart__inner">
    <div class="cart__inner-close">Close</div>
    <div class="cart__inner-bg"></div>
    <div class="cart-items"></div>
    <div class="cart-total cart-grid">
      <div class="cart-total__inner">
        <div class="cart-total__label">Total:</div>
        <div class="cart-total__amount">€ 0</div>
        <div class="cart-total__taxes"> Delivery fee and tax <br> calculated at checkout </div>
        <a class="cart-total__checkout-btn button" href="#">Checkout</a>
      </div>
    </div>
  </div>
</aside>

Struktur HTML sangat sederhana. Membuat kisi-kisi CSS untuk mengatur tampilan produk dengan cepat dan membuat pembungkus untuk gambar utama dan galeri di setiap proyek. Alasan pembuatan wrapper adalah untuk memiliki a Elemen tunggal dengan ketinggian tetapmemungkinkan semua gambar di dalamnya berskala hingga 100% dari ukuran induknya, sehingga pengelolaan responsif juga menjadi lebih mudah.

Pada titik ini, keputusan pertama muncul: haruskah saya membuat semua node gambar langsung di dalam markup atau hanya melampirkan gambar galeri saat tombol diklik? Pendekatan pertama membuat HTML lebih bertele-tele dan meningkatkan jumlah node pada halaman, sedangkan pendekatan kedua mengharuskan pembuatan semua gambar saat runtime dan menambahkannya ke node, sehingga menunda dimulainya animasi dan berpotensi mengakibatkan pengelolaan dua gambar. masalah dengan antrian proses.

Jadi saya memilih Sertakan semua gambar langsung dalam HTML. Pilihan ini juga membantu mengatasi kemungkinan masalah tambahan: dengan mengambil semua node img, saya dapat melakukannya Pramuat semua gambar Selama fase pemuatan awal, preloader masih terlihat.

Oke, HTML sudah siap; saatnya melanjutkan dan membuat kelas untuk mengelola produk.

Kategori “Produk”.

Struktur kelas Produk sangat sederhana dan terutama menangani:

  1. mengenali Koordinat x dan y keranjang belanja Dalam judulnya, inilah poin yang akan dituju oleh animasi tersebut;
  2. tambahkan sebuah klik pendengar Atur elemen pada CTA dan mulai animasi;
  3. Buat animasi garis waktu;
  4. mengatur ulang Elemen setelah animasi selesai.
export default class Products {
	constructor() {
		this.products = [...document.querySelectorAll('.products__item')];
		this.ctas = [...document.querySelectorAll('.products__cta')];
		this.cartButton = document.querySelector('.cart-button');

		this.cartButtonCoords = { x: 0, y: 0 };

		this.currentProduct = null;
		this.currentGallery = [];
		this.otherProducts = [];
		this.isTopRow = false;

		this.init();
	}


	init() {
		this.setCartButtonCoords();

		this.ctas.forEach((cta, i) => {
			cta.addEventListener('click', () => {
				this.currentProduct = this.products[i];
				this.otherProducts = this.products.filter((prod, index) => index !== i);
				this.currentGallery = [...this.currentProduct.querySelectorAll('.products__gallery-item')];
				this.isTopRow = window.innerWidth > 768 && i < 3;

				this.addToCart();
			})
		})


		window.addEventListener('resize', debounce(() => {
			this.setCartButtonCoords();
		}))
	}


	setCartButtonCoords() {
        const { x, y } = this.cartButton.getBoundingClientRect();
		this.cartButtonCoords = { x, y };
	}

...

Mari kita uraikan metode init dengan cepat:

  1. ini this.setCartButtonCoords metode disebut, yang adil Ambil koordinat x dan y Penggunaan tombol di header getBoundingClientRect();
  2. Buat pendengar klik untuk CTA, di mana animasi akan dijalankan. Metode ini sederhana: metode ini hanya mendefinisikan nilai konstruktor yang berisi item saat ini yang akan dianimasikan, item lain yang perlu dihilangkan, galeri aktif yang akan dianimasikan, dan this.isTopRow Bidang yang digunakan untuk menentukan arah animasi;
  3. Buat pendengar untuk mendengarkan Ubah ukuran acarayang mengatur ulang koordinat keranjang setiap kali ukuran layar berubah. Fungsi debounce mengoptimalkan fitur ini dengan mencegah metode berjalan pada setiap pengubahan ukuran piksel, dan malah memicunya setelah waktu habis di akhir operasi pengubahan ukuran browser.

Sekarang, mari kita ke bagian yang menyenangkan: this.addToCart metode, di antaranya Garis waktu GSAP dibuat.

Animasi “Tambahkan ke troli”.

Mari kita mulai dengan dasar-dasarnya dan meninjau evolusi garis waktu selangkah demi selangkah.

Langkah pertama adalah menyorot produk yang dipilih dan menghilangkan item lainnya, lalu mengembalikan semuanya ke keadaan semula setelah animasi selesai.

tl.to(this.otherProducts, {
   scale: 0.8, autoAlpha: 0.05, duration: 0.6, stagger: 0.04, ease: 'power2.out',
}, 'start');
  
tl.to(this.currentProduct, {
  scale: 1.05, duration: 1, ease: 'power2.out',
}, 'start+=0.7');


tl.to([this.currentProduct, this.otherProducts], {
  scale: 1, autoAlpha: 1, duration: 0.8, stagger: 0.03, ease: 'power2.out',
}, 'start+=1.6');

Ide dibalik animasi ini adalah untuk memindahkan elemen menuju koordinat keranjang, jadi langkah pertama dalam timeline adalah koordinat x dan y dari gambar tween galeri.

tl.to(this.currentGallery, {
  x: this.cartButtonCoords.x,
  y: this.cartButtonCoords.y,
  stagger: {
    from: 'end',
    each: 0.04,
  },
  duration: 1.8,
  ease: 'power2.inOut'
});

Kita langsung dihadapkan pada masalah pertama: gambar bergerak ke bawah, bukannya ke atas seperti yang kita harapkan. Alasannya sederhana: kita menambahkan koordinat kereta ke koordinat gambar saat ini.

Oleh karena itu, tujuan kami adalah Hitung jarak antara gambar dan lokasi keranjangdan kurangi jarak tersebut selama tweening. Untuk melakukan ini, sebelum menginisialisasi garis waktu, kita mengambil koordinat kanan dan y dari gambar saat ini dan menguranginya dari koordinat kereta.

const { y, right} = this.currentGallery[0].getBoundingClientRect();

tl.to(this.currentGallery, {
  x: this.cartButtonCoords.x - right,
  y: this.cartButtonCoords.y - y,

  ...

Sekarang, seperti yang bisa kita lihat, gambar bergerak ke arah tombol yang benar. Mari kita lakukan ini dengan menambahkan a Efek memudarnya gambar Saat mereka mendekati posisi akhirnya, sesuaikan skala dan autoAlpha ciri.

tl.to(this.currentGallery, {
  x: this.cartButtonCoords.x - right,
  y: this.cartButtonCoords.y - y,
  scale: 0,
  autoAlpha: 0,
  stagger: {
    from: 'end',
    each: 0.04,
  },
  duration: 1.8,
  ease: 'power2.inOut'
}, 'start');

Nah, dengan menyesuaikan durasi dan pelonggaran timeline, ini mungkin sudah memberikan hasil yang bagus, namun ide yang muncul di benak saya adalah membuat lintasan yang lebih kompleks.

Jadi saya berpikir untuk membagi timeline menjadi dua langkah: Pada langkah pertama, gambar keluar dari bingkai, dan pada langkah kedua, gambar masuk ke keranjang belanja.

ini tempatnya rencana induk perencanaan bingkai utama Datang dan selamatkan aku!

Langkah pertama adalah kembali ke awal animasi dan menggunakannya getBoundingClientRect() metode. Kemudian, gunakan nilai ini untuk memindahkan gambar 150% pada 40% animasi, lalu arahkan gambar ke keranjang selama 60% animasi berikutnya.

tl.to(this.currentGallery, {
  keyframes: {
    '40%': {
      y: height * 1.5,
      scale: 0.8,
      autoAlpha: 1,
    },
    '100%': {
      x: this.cartButtonCoords.x - right,
      y: this.cartButtonCoords.y - y,
      scale: 0,
      autoAlpha: 0,
    },
  },
  stagger: {
    from: 'end',
    each: 0.04,
  },
  duration: 1.8,
  ease: 'power2.inOut',
}, 'start');

Ini adalah hasil akhirnya, tetapi pada titik ini muncul masalah lain: animasi di baris atas berfungsi dengan baik, tetapi efek di baris bawah hilang.

Begitu dekat namun begitu jauh.

Oke, jadi apa yang kita lakukan dengan animasi di baris paling bawah? melewati arah sebaliknya: Daripada bergerak ke bawah, mereka akan mengambil jalur sebaliknya, mula-mula melepaskan diri ke atas lalu bergerak menuju gerobak.
Jadi, mari kita mulai menggunakannya this.isTopRowkita buat di konstruktor untuk menentukan apakah animasi melibatkan item di baris atas atau bawah.

Langkah pertama melibatkan transformOrigin gambar.

gsap.set(this.currentGallery, { transformOrigin: this.isTopRow ? 'top right' : 'bottom left' });

Kami kemudian melanjutkan untuk mengubah orientasi dalam bingkai utama sambil mengambil posisi kiri menggunakan nilai awal getBoundingClientRect()

const { y, left, right, height } = this.currentGallery[0].getBoundingClientRect();

...

keyframes: {
  '40%': {
    y: this.isTopRow ? height * 1.5 : -height * 1.5,
    scale: this.isTopRow ? 0.8 : 0.5,
    autoAlpha: 1,
  },
  '100%': {
    x: this.isTopRow ? this.cartButtonCoords.x - right : this.cartButtonCoords.x - left,
    y: this.isTopRow ? this.cartButtonCoords.y - y : this.cartButtonCoords.y - y - height,
    scale: 0,
    autoAlpha: 0,
  },
},

Oke, kita hampir sampai! Animasi baris paling bawah masih memiliki sedikit kekurangan yang disebabkan oleh transformOrigin Kita baru saja mengaturnya di awal timeline.

Untuk mengoreksi poin akhir secara visual, kami akan mengurangi nilai arbitrer dari target animasi yang sesuai dengan ukuran lencana jumlah item keranjang belanja.

'100%': {
  x: this.isTopRow ? this.cartButtonCoords.x - right : this.cartButtonCoords.x - left - 12, // removing half button width
  y: this.isTopRow ? this.cartButtonCoords.y - y : this.cartButtonCoords.y - y - height + 25, // adding full button height
  scale: 0,
  autoAlpha: 0,
},

Ini adalah hasil akhirnya:

Sekarang, mari kita reset animasi di akhir timeline:

onComplete: () => {
   gsap.set(this.currentGallery, { scale: 1, autoAlpha: 1, y: 0, x: 0 });
   gsap.set(gallery, { autoAlpha: 0 });
   this.resetAnimation()
 },

Kita cukup mengembalikan elemen galeri yang baru saja dianimasikan ke posisi semula (tumpang tindih sepenuhnya dengan gambar utama yang masih terlihat, jadi tidak ada perbedaan mencolok), atur opacity ke 0, dan jalankan metode objek yang jelas di konstruktor.

resetAnimation() {
   this.currentProduct = null;
   this.currentGallery = [];
   this.otherProducts = [];
 }

ini fungsi pengaturan ulang Ini bahkan mungkin tidak diperlukan karena array akan ditimpa setiap kali peristiwa klik diaktifkan pada CTA. Namun, jika kita tidak perlu lagi menggunakan elemen-elemen yang terdapat dalam array, lebih baik biarkan array tersebut kosong.

Oke, apakah kita sudah selesai? Menurutku belum, kita masih perlu merawat mobilnya!

Jangan biarkan hal-hal belum selesai.

Kelas Cart dibagi menjadi dua blok logika selama proses pengembangan: blok logika pertama hanya melibatkan adegan pembelian, dan blok logika kedua hanya berfokus pada logika animasi masuk dan keluar beragampengetahuan.

Mari kita mulai dengan skenario manajemen produk:

addItemToCart(el) {
  const { id, price, name, cover } = el.dataset;

  const index = this.cartItems.findIndex((el) => el.id === id);

  if (index < 0) {
    const newItem = { id, price, name, cover, quantity: 1 };
    this.cartItems.push(newItem);

    const newCartItem = this.appendItem(newItem);
    this.cartItemsList.append(newCartItem);
  } else this.cartItems[index].quantity += 1;

  this.updateCart();
}

Cara menambahkan produk ke keranjang belanja sangat sederhana. Logikanya juga dibagi menjadi dua skenario:

  1. CTA kliknya untuk produk baru;
  2. CTA yang diklik adalah untuk produk Sudah ada di keranjang belanja.

Array this.cartItems di konstruktor mewakili daftar semua item yang ditambahkan ke keranjang dan oleh karena itu digunakan dalam metode untuk beralih di antara kemungkinan skenario. Jika produk belum ada di keranjang, produk tersebut dimasukkan ke dalam array this.cartItems, dan Node HTML dibuat Melalui metode this.appendItem. Jika produk sudah ada di keranjang, ambil saja berdasarkan indeksnya lalu Kuantitas telah diperbarui.

Mari kita lihat sekilas metode this.appendItem :

appendItem(item) {
  const cartItem = document.createElement('div');
  cartItem.classList.add('cart-item', 'cart-grid');
 
  cartItem.innerHTML = `
    <img class="cart-item__img" src="${item.cover}" alt="${item.name}">
 
    <div class="cart-item__details">
      <span class="cart-item__details-title">${item.name}</span>

      <button class="cart-item__remove-btn">Remove</button>

      <div class="cart-item__details-wrap">
        <span class="cart-item__details-label">Quantity:</span>

        <div class="cart-item__details-actions">
          <button class="cart-item__minus-button">-</button>
          <span class="cart-item__quantity">${item.quantity}</span>
          <button class="cart-item__plus-button">+</button>
        </div>
        <span class="cart-item__details-price">€ ${item.price}</span>
      </div>
    </div>
  `;

  const removeButton = cartItem.querySelector('.cart-item__remove-btn');
  const plusButton = cartItem.querySelector('.cart-item__plus-button');
  const minusButton = cartItem.querySelector('.cart-item__minus-button');

  removeButton.addEventListener('click', () => this.removeItemFromCart(item.id));
  plusButton.addEventListener('click', () => this.updateQuantity(item.id, 1));
  minusButton.addEventListener('click', () => this.updateQuantity(item.id, -1));


  return cartItem;
)

Selain menambahkan node HTML, saya juga menyiapkan semua pendengar untuk berbagai tombol yang membentuk UI, menghubungkannya ke metode masing-masing:

  • Tombol “Hapus” akan dijalankan this.removeItemFromCart(item.id) Metode menghapus objek dari array produk aktif dan node HTML.
  • Tombol “+” dan “-” mengubah jumlah produk di keranjang belanja dan menjalankan metode this.updateQuantity(item.id, 1 / -1), meneruskan jumlah yang akan ditambahkan atau dihapus sebagai parameter.

Di akhir setiap modifikasi keranjang (penambahan/penghapusan/perubahan jumlah) saya menetapkan a Metode pembaruan Ubah total pembayaran.

updateCart() {
  const cartElementsQuantities = [...document.querySelectorAll('.cart-item__quantity')];
  this.cartButtonNumber.innerHTML = Object.values(this.cartItems).length;
 
  let cartAmount = 0;

  Object.values(this.cartItems).forEach((item, i) => {
    cartElementsQuantities[i].innerHTML = item.quantity;
    cartAmount+= item.price * item.quantity
  })

  this.cartTotal.innerHTML = `€ ${cartAmount}`;
}

Kode ini dibuat untuk fungsionalitas dasar dan perlu diperluas agar berfungsi dengan baik dengan situs web e-commerce. Dalam kasus saya, setelah memilih platform Shopify, saya menggunakan Pembelian Shopify Perpustakaan untuk mengelola API dan menyinkronkan pembayaran keranjang dengan pembayaran terakhir di platform, tetapi setiap platform memiliki API sendiri untuk menangani hal ini.

Penerapan lain yang mungkin dilakukan, sedikit lebih kompleks namun jelas lebih ramah pengguna, adalah menyimpan produk yang ditambahkan ke keranjang penyimpanan lokalmemastikan halaman tetap ada di memori meskipun pengguna memuat ulang halaman.

Jadi langkah terakhir untuk menyelesaikan penambahan produk ke keranjang Anda adalah melakukan addItemToCart Metode dalam timeline yang dibuat sebelumnya.

tl.add(() => {
  Cart.addItemToCart(this.currentProduct);
}, 'start+=0.6');

Dengan cara ini, saat animasi gambar diputar, item saat ini juga akan dimasukkan ke dalam keranjang belanja.

Mengapa tidak menganimasikan tombol menggunakan jumlah item saat ini?

Mari kita bawa pulang.

dalam metode init Cart kelas, kita menginisialisasi tombol yang akan dianimasikan dengan mengatur elemen ke skala 0.

Kemudian masih di dalam keranjang belanja utama, tambahkan timeline yang cukup kita tambahkan this.cartButtonAnimationEnter Metode, tetapi hanya jika jumlah produk di keranjang belanja saat ini adalah 0.

tl.add(() => {
  if (Cart.cartItems.length === 0) Cart.cartButtonAnimationEnter();
  Cart.addItemToCart(this.currentProduct);
}, 'start+=0.6');

cartButtonAnimationEnter() {
  const tl = gsap.timeline();
  tl.addLabel('start');

  tl.to(this.cartButtonLabel, { x: -35, duration: 0.4, ease: 'power2.out' }, 'start');
  tl.to([this.cartButtonNumber, this.cartButtonBg], {
    scale: 1, stagger: 0.1, duration: 0.8, ease: 'elastic.out(1.3, 0.9)',
  }, 'start');
  return tl;
}

Sekarang sampai pada bagian terakhir, dan bagian paling menarik, yang melibatkan animasi masuk dan keluar keranjang belanja.

Jadi biarkan keluar dan biarkan masuk.

Masih dalam metode init Cart kelas, kami akan mengelola dua langkah dasar dari keseluruhan proses.
Langkah pertama adalah menjalankan fungsi pengaturan elemen animasi, termasuk tombol keranjang belanja dan animasi pembukaan keranjang belanja.

Langkah kedua adalah pendengar acara yang mengelola animasi masuk dan keluar berdasarkan interaksi keranjang dan tombol tutup:

init() {
   this.cartButtonAnimationSetup();
   this.cartAnimationSetup();


   this.cartButton.addEventListener('click', () => {
     if (this.isAnimating) return;
     document.body.classList.add('locked');

     this.isAnimating = true;

     this.cartAnimationEnter().then(() => {
       this.cartOpened = true;
       this.isAnimating = false;
     })
   })
  
   this.cartClose.addEventListener('click', () => {
     if (this.isAnimating) return;
     document.body.classList.remove('locked');

     this.isAnimating = true;

     this.cartAnimationLeave().then(() => {
       this.cartOpened = false;
       this.isAnimating = false;
     })
   })
 }

Mari kita lakukan analisis singkat:

  • this.isAnimating terbiasa Cegah dua garis waktu agar tidak tumpang tindih (Ini adalah pilihan gaya, bukan yang dipaksakan; alternatifnya adalah dengan menggunakan killTweensOf metode GSAP). Jika animasi sedang berlangsung, pembalikannya tidak dapat dipicu hingga animasi selesai;
  • Tambahkan kelas yang terkunci ke badan memblokir pengguliran;
  • Memicu animasi masuk/keluar, setelah itu nilainya this.isAnimating Dan this.cartOpened Sudah disetel.

Satu catatan kecil terakhir tentang animasi pintu masuk:

cartAnimationEnter() {
   this.animatingElements.items = [...this.cart.querySelectorAll('.cart-item')];
   if (this.animatingElements.items.length > 0) gsap.set(this.animatingElements.items, { x: 30, autoAlpha: 0 });

   const tl = gsap.timeline({
     onStart: () => gsap.set(this.cart, { xPercent: 0 })
   });
   tl.addLabel('start');

   tl.to([this.animatingElements.bg, this.animatingElements.innerBg], {
     xPercent: 0, stagger: 0.1, duration: 2.2, ease: 'expo.inOut',
   }, 'start');

   tl.to(this.animatingElements.close, {
     x: 0, autoAlpha: 1, stagger: 0.1, duration: 1, ease: 'power2.out',
   }, 'start+=1.3');
    if (this.animatingElements.items.length > 0) {
     tl.to(this.animatingElements.items, {
       x: 0, autoAlpha: 1, stagger: 0.1, duration: 1, ease: 'power2.out',
     }, 'start+=1.4');
   }
   if (this.animatingElements.noProds) {
     tl.to(this.animatingElements.noProds, {
       x: 0, autoAlpha: 1, stagger: 0.1, duration: 1, ease: 'power2.out',
     }, 'start+=1.4');
   }
    tl.to(this.animatingElements.total, {
     scale: 1, autoAlpha: 1, stagger: 0.1, duration: 1, ease: 'power2.out',
   }, 'start+=1.6');

   return tl;
 };

this.animatingElements.items tidak didefinisikan dalam this.cartAnimationSetup berfungsi karena Jumlah elemen berubah setiap waktu Mereka ditambahkan melalui animasi, yang hanya dipanggil selama inisialisasi kelas Cart.

Jika kita tidak menyetel elemen setiap kali menjalankan animasi entri, this.animatingElements.items selalu berupa array kosongjadi kami tidak pernah melihat item tersebut ditambahkan ke troli.

Membiarkan animasi cukup mengubah posisi elemen di luar tata letak:

cartAnimationLeave() {
  const tl = gsap.timeline({
    onComplete: () => gsap.set(this.cart, { xPercent: 100 })
  });
  tl.addLabel('start');

  tl.to([this.animatingElements.bg, this.animatingElements.innerBg], {
    xPercent: 110, stagger: 0.1, duration: 1.5, ease: 'expo.inOut',
  }, 'start');

  if (this.animatingElements.items.length > 0) {
    tl.to(this.animatingElements.items, {
      x: 30, autoAlpha: 0, stagger: 0.1, duration: 0.8, ease: 'power2.out',
    }, 'start');
  }
  if (this.animatingElements.noProds) {
    tl.to(this.animatingElements.noProds, {
      x: 30, autoAlpha: 0, stagger: 0.1, duration: 0.8, ease: 'power2.out',
    }, 'start');
  }

  tl.to(this.animatingElements.close, {
    x: 30, autoAlpha: 0, stagger: 0.1, duration: 0.8, ease: 'power2.out',
  }, 'start');

  tl.to(this.animatingElements.total, {
    scale: 0.9, autoAlpha: 0, stagger: 0.1, duration: 0.8, ease: 'power2.out',
  }, 'start');

  return tl;
}

Inilah hasil akhir dari animasi keranjang belanja!

Ah, mungkin Anda sudah mengetahuinya, tapi saya lupa menyebutkan bahwa saya adalah penggemar berat The Office dan…

rencana pengembangan website



metode pengembangan website

jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website

#Dari #produk #keranjang #Tambahkan #animasi #terpandu #pengalaman #berbelanja

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *