HanC's Blog

使用Vue.js自製響應式Swiper

Posted:27th September 2024
8 Minutes
1519 Words

前言

有時於專案中,需要製作卡片切換器,第一個想到的就是Swiperjs,而這切換器功能,其實不會很複雜,自己也能在短時間內,把需求實現出來,同時包含響應式功能。

本篇文章將實現的功能:

[基礎卡片]

  • 左右切換查看上下張名片
  • 當顯示的名片是第一張時,左邊的button呈現disabled,反之,顯示最後一張名片時,右邊的button呈現disabled。
  • 視窗於768px以下時,每次只顯示1張名片。視窗於768px以上時,每次顯示2張名片。
  • 滑鼠移至單張名片時,稍放大效果,增加使用者體驗的互動感

[自動切換]

  • 保留基本的左右切換鈕
  • 於2秒時,自動切換至下一張
  • 當輪播至最後一張時,下一個會回到第一張

[卡片旋轉]

  • 保留下一張按鈕
  • 點按卡片,翻轉至背面,再點一次,翻轉回正面 (使用滑鼠拖拉手勢,也能達成一樣效果)

基礎卡片 - 實作方式


首先將主要名片區塊以橫向排列,加上一些效果: class配置

  • transition-transform: 告訴瀏覽器對 transform 屬性變化添加過渡效果。
  • duration-500: 設定動畫過渡時間為 500 毫秒。
  • ease-in-out: 設定動畫過渡效果一開始和結束時速度較慢,中間速度較快,提供平滑過渡效果。

style配置 transform: translateX(-${currentIndex * 100 / visibleCards}%);

  • transform: CSS 的 transform 屬性用來移動或改變元素的形狀,這裡是通過 translateX 改變元素的橫向位移。
  • translateX: translateX 用於將元素在水平方向上移動,負值表示向左移動,正值表示向右移動。
  • ${currentIndex * 100 / visibleCards}: 將當前名片索引乘以 100,表示每次移動的百分比。如果每次顯示一個名片,則每個名片佔據 100% 的寬度,移動的百分比就是名片的索引值 * 100。
1
<div class="card-block overflow-hidden w-full">
2
<div class="flex transition-transform duration-500 ease-in-out"
3
:style="`transform: translateX(-${currentIndex * 100 / visibleCards}%);`">
4
....
5
</div>
6
</div>

再來是名片寬度,採用TailwindCSS的 Customizing Screens 標準,於不同視窗大小時,限制其單一名片最大寬度

  • md: 即表示 @media(min-width:768px){...},當螢幕大於768px時,此div的寬度只顯示父層的50%。
  • flex-shrink-0: 當螢幕在變動,空間不夠時,元素會有被擠壓的可能,因此也需要設定此屬性,以防止UI被壓縮時跑版。
1
<div v-for="(person, index) in people"
2
:key="index"
3
class="card flex-shrink-0 w-full md:w-1/2 p-4">
4
...
5
</div>

PreSlide Button,除了可以查看前一張名片外,當切換至第一張名片時,需使Button的樣式更新為disabled,才不會讓使用者無從得知目前的名片是否還有前一張可以繼續查閱

  • currentIndex更新時,會即時判斷其disabled值,以顯示button可使用狀態
1
<button @click="prevSlide"
2
:disabled="currentIndex === 0"
3
class="p-3 text-white rounded-lg"
4
:class="currentIndex === 0 ?
5
'cursor-not-allowed bg-gray-300':
6
'bg-gray-700 hover:bg-gray-600'">
7
<i class="fas fa-arrow-left"></i>
8
</button>
1
prevSlide() {
2
if (this.currentIndex > 0) {
3
this.currentIndex--;
4
}
5
}

NextSlide Button,與PreSlide Button類似

  • 最後一張卡片需以整個人名陣列的長度-1去取得,判斷其disalbed狀態
1
<button @click="nextSlide"
2
:disabled="currentIndex >= people.length - visibleCards"
3
class="p-3 text-white rounded-lg "
4
:class="currentIndex >= people.length - visibleCards ?
5
'cursor-not-allowed bg-gray-300':
6
'bg-gray-700 hover:bg-gray-600'">
7
<i class="fas fa-arrow-right"></i>
8
</button>
1
nextSlide() {
2
if (this.currentIndex < this.people.length - this.visibleCards) {
3
this.currentIndex++;
4
}

響應式顯示1張名片或2張名片

  • 於元素都已掛載後,加入監聽器,以接收螢幕寬度變化時的值判斷顯示1張名片或2張名片
1
const { createApp } = Vue;
2
3
createApp({
4
methods: {
5
updateVisibleCards() {
6
this.visibleCards = window.innerWidth >= 768 ? 2 : 1;
7
}
8
},
9
mounted() {
10
this.updateVisibleCards();
11
window.addEventListener('resize', this.updateVisibleCards);
12
},
13
beforeUnmount() {
14
window.removeEventListener('resize', this.updateVisibleCards);
15
}
1 collapsed line
16
}).mount('#app');

滑鼠移動時的互動感

  • hover:scale-105 duration-300 ease-in-out: 以平滑的方式將滑鼠hover到的區塊在兩個維度(水平和垂直)等比例放大 5%,讓使用者可以foucs在當前滑動到的名片
1
<div class="bg-white rounded-lg shadow-lg border
2
border-gray-200 p-6 transform transition-transform
3
hover:scale-105 duration-300 ease-in-out">
4
...
5
</div>

自動切換 - 實作方式


與基本卡片不同的點在: 底部添加進度條樣式

  • transition-all duration-300 ease-in-out: 對所有可變屬性(包括寬度、邊框等)進行過渡動畫,同時 設定過渡時間為 300 毫秒,讓切換更平滑,使之從 w-16 變為 w-8 時,具有「伸縮感」。
1
<div class="flex justify-center space-x-2 mt-4">
2
<div
3
v-for="(person, index) in people"
4
:key="index"
5
class="h-2 rounded-full border-2 border-gray-400
6
transition-all duration-300 ease-in-out"
7
:class="currentIndex === index ?
8
'bg-gray-600 border-gray-600 w-16' :
9
'bg-gray-200 w-8'"
10
></div>
11
</div>
  • 自動計時器,於每2秒時,切換到下一張名片
1
methods: {
2
startAutoplay() {
3
this.intervalId = setInterval(this.nextSlide, 2000);
4
},
5
stopAutoplay() {
6
clearInterval(this.intervalId);
7
}
8
},
9
10
mounted() {
11
this.startAutoplay();
12
},
13
beforeUnmount() {
14
this.stopAutoplay();
15
}

卡片旋轉 - 實作方式


點按時,切換當前卡片顯示正背面狀態

  • flipCard() :點擊觸發當前 isFlipped 值來改變卡片的狀態,從而觸發翻轉效果
  • transform-style: preserve-3d :讓子元素保持 3D 空間效果
  • .card.flipped .card-inner:當卡片的 .flipped 類別因flipCard()被添加時,觸發 rotateY(180deg) 翻轉效果,使卡片繞Y軸旋轉 180 度,顯示背面
1
<div v-for="(person, index) in people"
2
:key="index" class="card flex-shrink-0 w-full p-4"
3
:class="{'flipped': currentIndex === index && isFlipped}"
4
@click="flipCard">
5
...
6
</div>
1
flipCard() {
2
this.isFlipped = !this.isFlipped;
3
}
1
.card-inner {
2
position: relative;
3
width: 100%;
4
transform-style: preserve-3d;
5
transition: transform 0.6s ease;
6
}
7
8
.card.flipped .card-inner {
9
transform: rotateY(180deg);
10
}

翻轉後,正背面顯示效果

  • backface-visibility: hidden:用於隱藏當卡片旋轉後背面的內容,避免背面的元素在卡片翻轉時露出
1
.card-front,
2
.card-back {
3
height: 100%;
4
width: 100%;
5
backface-visibility: hidden;
6
}
7
8
/* 確保卡片的背面 在翻轉之前被隱藏並正確定位,
9
這樣當卡片進行翻轉動畫時,背面可以正常顯示出來 */
10
.card-back {
11
transform: rotateY(180deg);
12
}

結語

卡片切換器,可以運用於許多場景,例如論壇討論區、公司人員介紹、電商平台產品區塊…,當專案中使用到的swiper功能沒有需要太多功能或多元性,不一定要引入大包swiper.js,可透過自行製作來達成目標,若需擴增功能時,也能全權掌握任一細節,將swiper再往上提昇一階,增加此切換器的亮眼程度!

Article title:使用Vue.js自製響應式Swiper
Article author:HanC
Release time:27th September 2024