基于Vue3實現(xiàn)鼠標滑動和滾輪控制的輪播
需求
實現(xiàn)一個輪播組件,這個組件應該有以下的特性:
- 用戶可以通過鼠標點擊來切換輪播圖。
- 用戶可以通過鼠標滑動來切換輪播圖。
- 用戶可以通過鼠標滾輪來切換輪播圖。
- 一開始自動切換,用戶操作時停止自動切換,用戶停止操作一段時間后,繼續(xù)自動切換。
效果圖

分析與設計
數(shù)組結(jié)構(gòu):
interface ListType {
title: string
icon: string
img: string
desc: string[]
}
const list: ListType = []
輪播中有一個唯一的active項,設置其下標為activeIndex
<ul class="swipper-wrapper">
<li class="swipper-item"
v-for="(item, index) in list"
:class="{ active: index === activeIndex }"
@click="activeIndex = index"
>
<img class="swipper-item-icon" :src="item.icon" alt="">
{{ item.title }}
</li>
</ul>
const activeIndex = ref(0)
輪播最多可以同時展示7項,設置常量NUM默認值為7,理論上來說可以通過activeIndex獲取展示的這7項的下標(即取距離activeIndex最近的7項, 因為需要距離最近,所以暫定這里NUM為奇數(shù)):
const NUM = 7 // 展示的卡片數(shù)量(奇數(shù))
function getSurroundingNumbers(length: number, activeIndex: number) {
var start = activeIndex - (NUM - 1) / 2;
var end = activeIndex + (NUM + 1) / 2;
if (activeIndex < (NUM - 1) / 2) {
start = 0;
end = NUM;
}
if (activeIndex > length - (NUM + 1) / 2) {
start = length - NUM;
end = length;
}
var result = [];
for (var i = start; i < end; i++) {
result.push(i);
}
return result;
}
const surroundingNumbers = computed(() => getSurroundingNumbers(list.length, activeIndex.value))
比較每一項的下標和surroundingNumbers,知道他在數(shù)組的前面還是后面還是里面,從而設置相應的樣式:
function getSwiperItemStyle(index: number) {
// 每個卡片高度60px 間隔16px
// 最多正常展示NUM個,其余藏在后,只露出16px高度
const HIDEH = 15,
H = 60,
M = 16;
let y = 0,
scaleX = 1,
backgroundColor = '#f9fbfc',
zIndex = 9999,
boxShadow = '2px 2px 1px 0px rgba(0,0,0,0.04), #fff 0px -2px 0px 0px';
if (!surroundingNumbers.value.includes(index)) {
backgroundColor = '#EBEDF2'
zIndex = zIndex - Math.abs(activeIndex.value - index) + 2
scaleX = 0.98 ** (Math.abs(activeIndex.value - index) + 2)
if (index < surroundingNumbers.value[0]) {
// 在上面
y = HIDEH * index
scaleX = 0.98 ** (surroundingNumbers.value[0] - index)
boxShadow = '2px 2px 1px 0px rgba(255, 255, 255, .3) inset';
} else {
// 在下面
y = surroundingNumbers.value[0] * HIDEH + (NUM - 1) * (H + M) + (index - surroundingNumbers.value.at(-1)!) * HIDEH
scaleX = 0.98 ** (index - surroundingNumbers.value.at(-1)!)
boxShadow = '2px 2px 1px 0px rgba(0,0,0,0.04)';
}
} else {
y = surroundingNumbers.value[0] * HIDEH + (index - surroundingNumbers.value[0]) * (H + M)
}
if (activeIndex.value === index) {
boxShadow = '5px 6px 12px 0px rgba(0,0,0,0.08), #fff 0px -2px 0px 0px';
}
return {
transform: 'translateY(' + y / 19.2 + 'vw) scaleX(' + scaleX + ')',
backgroundColor,
zIndex,
boxShadow
}
}
<ul class="swipper-wrapper">
<li class="swipper-item"
v-for="(item, index) in list"
:class="{ active: index === activeIndex }"
:style="getSwiperItemStyle(index)"
@click="activeIndex = index"
>
<img class="swipper-item-icon" :src="item.icon" alt="">
{{ item.title }}
</li>
</ul>
交互實現(xiàn)
鼠標滾輪切換
綁定滾動事件@wheel="wheel"
// 處理鼠標滾動
function wheel(event: WheelEvent) {
event.preventDefault(); // 防止頁面滾動
if (event.deltaY < 0) {
// 向下滾動,activeIndex減少
activeIndex.value = Math.max(0, activeIndex.value - 1);
} else {
// 向上滾動,activeIndex增加
activeIndex.value = Math.min(list.length - 1, activeIndex.value + 1);
}
}
鼠標滑動切換
綁定鼠標事件@mousedown="startSwipe" @mousemove="swipe" @mouseup="endSwipe" @mouseleave="endSwipe"
// 處理滑動
let startY = 0; // 鼠標按下時的y坐標
let accumulatedDiffY = 0; // 累積的滑動距離
let swiping = false; // 是否正在滑動
function startSwipe(event: MouseEvent) {
startY = event.clientY;
swiping = true;
}
function swipe(event: MouseEvent) {
if (!swiping) return;
const currentY = event.clientY;
const diffY = currentY - startY;
// 累積滑動距離
accumulatedDiffY += diffY;
// 假設每個元素的高度是100px
const elementHeight = 84;
// 使用累積的滑動距離計算滑動的元素數(shù)量,向下取整
const numItems = Math.floor(Math.abs(accumulatedDiffY) / elementHeight);
if (accumulatedDiffY > 0) {
// 向下滑動,activeIndex減少
if (activeIndex.value <= 2) return
activeIndex.value = Math.min(list.length - 3, activeIndex.value)
activeIndex.value = Math.max(0, activeIndex.value - numItems);
accumulatedDiffY -= numItems * elementHeight; // 更新累積的滑動距離
} else {
// 向上滑動,activeIndex增加
if (activeIndex.value >= list.length - 3) return
activeIndex.value = Math.max(2, activeIndex.value)
activeIndex.value = Math.min(list.length - 1, activeIndex.value + numItems);
accumulatedDiffY += numItems * elementHeight; // 更新累積的滑動距離
}
// 更新起始Y坐標
startY = currentY;
}
function endSwipe(event: MouseEvent) {
swiping = false;
accumulatedDiffY = 0; // 結(jié)束滑動時重置累積的滑動距離
}自動切換
// 自動切換定時器
const { pause, resume } = useIntervalFn(() => {
activeIndex.value++
if (activeIndex.value >= list.length - 1) {
activeIndex.value = 0
}
}, 3000)
// 重新自動切換的倒計時
const { start: startTimeoutFn, stop: stopTimeoutFn } = useTimeoutFn(resume, 5000)
在每個用戶操作的方法里先暫停自動切換pause()并停止繼續(xù)切換的倒計時stopTimeoutFn,結(jié)束操作時再開啟繼續(xù)切換的倒計時startTimeoutFn。
到此這篇關(guān)于基于Vue3實現(xiàn)鼠標滑動和滾輪控制的輪播的文章就介紹到這了,更多相關(guān)Vue3輪播內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue.js實現(xiàn)點擊后動態(tài)添加class及刪除同級class的實現(xiàn)代碼
這篇文章主要介紹了vue.js實現(xiàn)點擊后動態(tài)添加class及刪除同級class的相關(guān)資料,需要的朋友可以參考下2018-04-04
vue3如何將html元素變成canvas(海報生成),進行圖片保存/截圖
這篇文章主要介紹了vue3實現(xiàn)將html元素變成canvas(海報生成),進行圖片保存/截圖,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
vue基于mint-ui實現(xiàn)城市選擇三級聯(lián)動
這篇文章主要為大家詳細介紹了vue基于mint-ui實現(xiàn)城市選擇三級聯(lián)動,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04

