基于Vue3實(shí)現(xiàn)鼠標(biāo)滑動(dòng)和滾輪控制的輪播
需求
實(shí)現(xiàn)一個(gè)輪播組件,這個(gè)組件應(yīng)該有以下的特性:
- 用戶可以通過(guò)鼠標(biāo)點(diǎn)擊來(lái)切換輪播圖。
- 用戶可以通過(guò)鼠標(biāo)滑動(dòng)來(lái)切換輪播圖。
- 用戶可以通過(guò)鼠標(biāo)滾輪來(lái)切換輪播圖。
- 一開(kāi)始自動(dòng)切換,用戶操作時(shí)停止自動(dòng)切換,用戶停止操作一段時(shí)間后,繼續(xù)自動(dòng)切換。
效果圖
分析與設(shè)計(jì)
數(shù)組結(jié)構(gòu):
interface ListType { title: string icon: string img: string desc: string[] } const list: ListType = []
輪播中有一個(gè)唯一的active項(xiàng),設(shè)置其下標(biāo)為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)
輪播最多可以同時(shí)展示7項(xiàng),設(shè)置常量NUM默認(rèn)值為7,理論上來(lái)說(shuō)可以通過(guò)activeIndex獲取展示的這7項(xiàng)的下標(biāo)(即取距離activeIndex最近的7項(xiàng), 因?yàn)樾枰嚯x最近,所以暫定這里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))
比較每一項(xiàng)的下標(biāo)和surroundingNumbers
,知道他在數(shù)組的前面還是后面還是里面,從而設(shè)置相應(yīng)的樣式:
function getSwiperItemStyle(index: number) { // 每個(gè)卡片高度60px 間隔16px // 最多正常展示NUM個(gè),其余藏在后,只露出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>
交互實(shí)現(xiàn)
鼠標(biāo)滾輪切換
綁定滾動(dòng)事件@wheel="wheel"
// 處理鼠標(biāo)滾動(dòng) function wheel(event: WheelEvent) { event.preventDefault(); // 防止頁(yè)面滾動(dòng) if (event.deltaY < 0) { // 向下滾動(dòng),activeIndex減少 activeIndex.value = Math.max(0, activeIndex.value - 1); } else { // 向上滾動(dòng),activeIndex增加 activeIndex.value = Math.min(list.length - 1, activeIndex.value + 1); } }
鼠標(biāo)滑動(dòng)切換
綁定鼠標(biāo)事件@mousedown="startSwipe" @mousemove="swipe" @mouseup="endSwipe" @mouseleave="endSwipe"
// 處理滑動(dòng) let startY = 0; // 鼠標(biāo)按下時(shí)的y坐標(biāo) let accumulatedDiffY = 0; // 累積的滑動(dòng)距離 let swiping = false; // 是否正在滑動(dòng) function startSwipe(event: MouseEvent) { startY = event.clientY; swiping = true; } function swipe(event: MouseEvent) { if (!swiping) return; const currentY = event.clientY; const diffY = currentY - startY; // 累積滑動(dòng)距離 accumulatedDiffY += diffY; // 假設(shè)每個(gè)元素的高度是100px const elementHeight = 84; // 使用累積的滑動(dòng)距離計(jì)算滑動(dòng)的元素?cái)?shù)量,向下取整 const numItems = Math.floor(Math.abs(accumulatedDiffY) / elementHeight); if (accumulatedDiffY > 0) { // 向下滑動(dòng),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; // 更新累積的滑動(dòng)距離 } else { // 向上滑動(dòng),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; // 更新累積的滑動(dòng)距離 } // 更新起始Y坐標(biāo) startY = currentY; } function endSwipe(event: MouseEvent) { swiping = false; accumulatedDiffY = 0; // 結(jié)束滑動(dòng)時(shí)重置累積的滑動(dòng)距離 }
自動(dòng)切換
// 自動(dòng)切換定時(shí)器 const { pause, resume } = useIntervalFn(() => { activeIndex.value++ if (activeIndex.value >= list.length - 1) { activeIndex.value = 0 } }, 3000) // 重新自動(dòng)切換的倒計(jì)時(shí) const { start: startTimeoutFn, stop: stopTimeoutFn } = useTimeoutFn(resume, 5000)
在每個(gè)用戶操作的方法里先暫停自動(dòng)切換pause()
并停止繼續(xù)切換的倒計(jì)時(shí)stopTimeoutFn
,結(jié)束操作時(shí)再開(kāi)啟繼續(xù)切換的倒計(jì)時(shí)startTimeoutFn
。
到此這篇關(guān)于基于Vue3實(shí)現(xiàn)鼠標(biāo)滑動(dòng)和滾輪控制的輪播的文章就介紹到這了,更多相關(guān)Vue3輪播內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue.js實(shí)現(xiàn)點(diǎn)擊后動(dòng)態(tài)添加class及刪除同級(jí)class的實(shí)現(xiàn)代碼
這篇文章主要介紹了vue.js實(shí)現(xiàn)點(diǎn)擊后動(dòng)態(tài)添加class及刪除同級(jí)class的相關(guān)資料,需要的朋友可以參考下2018-04-04vue項(xiàng)目中使用require.context引入組件
本文主要介紹了vue項(xiàng)目中使用require.context引入組件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08vue3如何將html元素變成canvas(海報(bào)生成),進(jìn)行圖片保存/截圖
這篇文章主要介紹了vue3實(shí)現(xiàn)將html元素變成canvas(海報(bào)生成),進(jìn)行圖片保存/截圖,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05vue中解決微信html5原生ios虛擬鍵返回不刷新問(wèn)題
這篇文章主要介紹了vue中解決微信html5原生ios虛擬鍵返回不刷新問(wèn)題,本文給大家分享解決方法,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10vue普通加密及國(guó)密SM2、SM3和sm4的使用例子
在我的項(xiàng)目中,甲方要求系統(tǒng)登錄時(shí)對(duì)密碼進(jìn)行加密后再傳給后端,指定使用國(guó)密SM3,下面這篇文章主要給大家介紹了關(guān)于vue普通加密及國(guó)密SM2、SM3和sm4使用的相關(guān)資料,需要的朋友可以參考下2022-12-12vue基于mint-ui實(shí)現(xiàn)城市選擇三級(jí)聯(lián)動(dòng)
這篇文章主要為大家詳細(xì)介紹了vue基于mint-ui實(shí)現(xiàn)城市選擇三級(jí)聯(lián)動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04vue3+ts 兄弟組件之間傳值的實(shí)現(xiàn)
Vue3是一款流行的前端框架,它支持多種傳值方式,包括兄弟組件之間的傳值,本文主要介紹了vue3+ts 兄弟組件之間傳值的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11