前言
有時於專案中,需要製作卡片切換器,第一個想到的就是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>
1prevSlide() {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>
1nextSlide() {2 if (this.currentIndex < this.people.length - this.visibleCards) {3 this.currentIndex++;4 }
響應式顯示1張名片或2張名片
- 於元素都已掛載後,加入監聽器,以接收螢幕寬度變化時的值判斷顯示1張名片或2張名片
1const { createApp } = Vue;2
3createApp({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 border2 border-gray-200 p-6 transform transition-transform3 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 <div3 v-for="(person, index) in people"4 :key="index"5 class="h-2 rounded-full border-2 border-gray-4006 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秒時,切換到下一張名片
1methods: {2 startAutoplay() {3 this.intervalId = setInterval(this.nextSlide, 2000);4 },5 stopAutoplay() {6 clearInterval(this.intervalId);7 }8},9
10mounted() {11 this.startAutoplay();12},13beforeUnmount() {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>
1flipCard() {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再往上提昇一階,增加此切換器的亮眼程度!