基于Vue實(shí)現(xiàn)卡片無(wú)限滾動(dòng)動(dòng)畫
概要設(shè)計(jì)
設(shè)置css的animation在適當(dāng)?shù)臅r(shí)間點(diǎn)重置動(dòng)畫來(lái)現(xiàn)實(shí)視覺(jué)上無(wú)限滾動(dòng)的效果。
詳細(xì)設(shè)計(jì)
計(jì)算動(dòng)畫中所需預(yù)設(shè)的卡片dom節(jié)點(diǎn)個(gè)數(shù)(即視覺(jué)上最多能看到的卡片個(gè)數(shù)),如圖1視窗高度為120px,卡片高度為56px,上下margin均為24px,因此一張卡片最多占據(jù)56+24*2=104px。當(dāng)卡片處于視窗垂直居中的位置時(shí),卡片上下各有(120-104)/2 = 8px的高度來(lái)承載其余卡片,因此所需預(yù)設(shè)卡片dom節(jié)點(diǎn)個(gè)數(shù)為3個(gè)。
圖1
圖2
設(shè)置動(dòng)畫的運(yùn)動(dòng)軌跡和持續(xù)時(shí)間來(lái)達(dá)到視覺(jué)上循環(huán)滾動(dòng)的效果。如圖3初始狀態(tài)下第一張卡片位置處于距離視窗頂部24px的位置,當(dāng)?shù)诙埧ㄆ\(yùn)動(dòng)到距離視窗頂部24px的位置時(shí)重置動(dòng)畫即可達(dá)到視覺(jué)上連續(xù)滾動(dòng)的效果。計(jì)算卡片需要向上滾動(dòng)的公式為【初始距頂部距離 - 目標(biāo)距頂部距離】即56+24*2-24=80px,因此設(shè)置動(dòng)畫幀的目標(biāo)位置為transform: translateY(-80px)。
代碼實(shí)現(xiàn):
<template> <div class="wrapper"> <div class="container"> <div class="content" v-for="i in 3" :key="i"> <img class="image" src="https://static-legacy.dingtalk.com/media/lADPDhmOwd9ZBQXNA8DNA8A_960_960.jpg" alt="" /> <span>點(diǎn)五已獲得500元京東卡</span> </div> </div> </div> </template> <script lang="ts"> import { defineComponent } from "vue"; export default defineComponent({ setup() {}, }); </script> <style scoped> .wrapper { height: 500px; display: flex; align-items: center; justify-content: center; } .container { width: 300px; height: 120px; overflow: hidden; background: #ccc; } .content { width: 290px; height: 56px; display: flex; margin: 24px auto; background: #fff; border-radius: 100px; flex-flow: row nowrap; align-items: center; backdrop-filter: blur(4px); animation: scroll 3s linear 1s infinite; } .image { width: 36px; height: 36px; margin: auto 10px; border-radius: 100%; } span { font-family: PingFang SC; font-size: 20px; line-height: 20px; text-align: center; } @keyframes scroll { from { transform: translateY(0); } to { transform: translateY(-80px); } } </style>
效果展示:
進(jìn)階功能
動(dòng)態(tài)更新卡片的數(shù)據(jù)
功能分析
卡片仍保持無(wú)限滾動(dòng),但每張卡片展示的數(shù)據(jù)均不同,也就是說(shuō)滾動(dòng)時(shí)視窗內(nèi)的dom節(jié)點(diǎn)不是每次都相同的。
概要設(shè)計(jì)
基于上述功能分析由此可以衍生出兩個(gè)設(shè)計(jì)思路:
- 展示多少條數(shù)據(jù)就渲染多少個(gè)dom節(jié)點(diǎn),簡(jiǎn)單粗暴無(wú)需考慮動(dòng)態(tài)更新數(shù)據(jù)問(wèn)題;當(dāng)數(shù)據(jù)量大時(shí)會(huì)導(dǎo)致頁(yè)面性能變差,除非數(shù)據(jù)量較少否則不推薦。
- 固定dom節(jié)點(diǎn)數(shù),在dom節(jié)點(diǎn)移動(dòng)到視窗之外即進(jìn)行數(shù)據(jù)更新;需保證修改dom節(jié)點(diǎn)時(shí)不會(huì)導(dǎo)致視窗內(nèi)的卡片閃爍;(采納)
詳細(xì)設(shè)計(jì)
1.根據(jù)當(dāng)前視窗寬高以及卡片寬高,設(shè)置六個(gè)卡片dom節(jié)點(diǎn),并將每三張卡片分一個(gè)塊,這里分兩個(gè)塊為card1、card2(如下圖)。
2.將兩個(gè)塊的位置重疊(為了方便設(shè)計(jì)動(dòng)畫),card2向上移動(dòng)(56+24)*3=240px的距離。
3.計(jì)算card2動(dòng)畫需要延遲執(zhí)行的時(shí)間。這里我設(shè)置每個(gè)card塊動(dòng)畫移動(dòng)距離為480px,執(zhí)行時(shí)間為12s,那么card2延遲執(zhí)行的時(shí)間公示為【延遲距離(card高度)/速度】即240/480/12 = 6s。所以動(dòng)畫幀設(shè)置為
@keyframes scroll1 { from { transform: translateY(0); left: 0; } to { transform: translateY(-480px); } } @keyframes scroll2 { from { transform: translateY(-240px); left: 0; } to { transform: translateY(-720px); } } .test1 { animation: scroll1 12s linear 0s infinite; margin-top: 120px; } .test2 { animation: scroll2 12s linear 6s infinite; transform: translateY(-240px); }
4.監(jiān)聽(tīng)card塊,當(dāng)card塊移出視窗時(shí),觸發(fā)dom節(jié)點(diǎn)刷新。監(jiān)聽(tīng)使用IntersectionObserver這個(gè)api。
@keyframes scroll1 { from { transform: translateY(0); left: 0; } to { transform: translateY(-480px); } } @keyframes scroll2 { from { transform: translateY(-240px); left: 0; } to { transform: translateY(-720px); } } .test1 { animation: scroll1 12s linear 0s infinite; margin-top: 120px; } .test2 { animation: scroll2 12s linear 6s infinite; transform: translateY(-240px); }
完整代碼
以下為完整代碼:
<template> <div class="wrapper"> <div class="container"> <div class="test1" id="card1"> <div class="content" v-for="item in cardArr1" :key="item"> <img class="image" src="https://static-legacy.dingtalk.com/media/lADPDhmOwd9ZBQXNA8DNA8A_960_960.jpg" alt="" /> <span> {{ item }}已獲得500元京東卡 </span> </div> </div> <div class="test2" id="card2"> <div class="content" v-for="item in cardArr2" :key="item"> <img class="image" src="https://static-legacy.dingtalk.com/media/lADPDhmOwd9ZBQXNA8DNA8A_960_960.jpg" alt="" /> <span> {{ item }}已獲得100元京東卡 </span> </div> </div> </div> </div> </template> <script lang="ts"> import { ref, defineComponent, onMounted } from "vue"; export default defineComponent({ setup() { let firsrObserver = true; let data: any = []; for (let i = 1; i <= 10; i++) { data.push(i); } const cardArr1 = ref<Array<any>>(data.slice(0, 3)); const cardArr2 = ref<Array<any>>(data.slice(3, 6)); console.log(cardArr1.value, cardArr2.value); let currentIndex = 6; const getNewData = () => { if (currentIndex + 3 <= data.length) { return data.slice(currentIndex, (currentIndex = currentIndex + 3)); } else { let rst = data.slice(currentIndex); currentIndex = 3 - rst.length; Array.prototype.push.apply(rst, data.slice(0, currentIndex)); return rst; } }; const observer2 = new IntersectionObserver((entry: any) => { // 第三第四第五加載新數(shù)據(jù) if (!entry[0].isIntersecting) { console.log(entry); if (entry[0].target.id === "card1") { cardArr1.value.splice(0, 3, ...getNewData()); } else { cardArr2.value.splice(0, 3, ...getNewData()); } console.log(cardArr1.value, cardArr2.value); } firsrObserver = false; }); onMounted(() => { observer2.observe(document.getElementById("card1") as HTMLElement); observer2.observe(document.getElementById("card2") as HTMLElement); }); return { data, cardArr1, cardArr2, }; }, }); </script> <style scoped> .wrapper { height: 500px; display: flex; align-items: center; /* background: pink; */ justify-content: center; } .container { width: 300px; height: 120px; overflow: hidden; background: #ccc; } .test1 { animation: scroll1 12s linear 0s infinite; margin-top: 120px; } .test2 { animation: scroll2 12s linear 6s infinite; transform: translateY(-240px); } .content { width: 290px; height: 56px; display: flex; margin: 24px auto; background: #fff; border-radius: 100px; flex-flow: row nowrap; align-items: center; backdrop-filter: blur(4px); } .image { width: 36px; height: 36px; margin: auto 10px; border-radius: 100%; } span { font-family: PingFang SC; font-size: 20px; line-height: 20px; text-align: center; } @keyframes scroll1 { from { transform: translateY(0); left: 0; } to { transform: translateY(-480px); } } @keyframes scroll2 { from { transform: translateY(-240px); left: 0; } to { transform: translateY(-720px); } } </style>
效果展示:
以上就是基于Vue實(shí)現(xiàn)實(shí)現(xiàn)卡片無(wú)限滾動(dòng)動(dòng)畫的詳細(xì)內(nèi)容,更多關(guān)于Vue無(wú)限滾動(dòng)動(dòng)畫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Vue 無(wú)限滾動(dòng)加載指令實(shí)現(xiàn)方法
- Vue.js 的移動(dòng)端組件庫(kù)mint-ui實(shí)現(xiàn)無(wú)限滾動(dòng)加載更多的方法
- vue實(shí)現(xiàn)滾動(dòng)加載的表格
- Vue實(shí)現(xiàn)下拉滾動(dòng)加載數(shù)據(jù)的示例
- 通過(guò)原生vue添加滾動(dòng)加載更多功能
- vue 使用鼠標(biāo)滾動(dòng)加載數(shù)據(jù)的例子
- vue指令做滾動(dòng)加載和監(jiān)聽(tīng)等
- 簡(jiǎn)單方法實(shí)現(xiàn)Vue?無(wú)限滾動(dòng)組件示例
- 基于Vue3實(shí)現(xiàn)無(wú)限滾動(dòng)組件的示例代碼
- 手寫vue無(wú)限滾動(dòng)指令的詳細(xì)過(guò)程
- Vue中實(shí)現(xiàn)滾動(dòng)加載與無(wú)限滾動(dòng)
相關(guān)文章
Vue3解析markdown并實(shí)現(xiàn)代碼高亮顯示的詳細(xì)步驟
Vue的markdown解析庫(kù)有很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等,這篇文章主要介紹了Vue3解析markdown并實(shí)現(xiàn)代碼高亮顯示,需要的朋友可以參考下2022-07-07詳解vue 在移動(dòng)端體驗(yàn)上的優(yōu)化解決方案
這篇文章主要介紹了vue 在移動(dòng)端體驗(yàn)上的優(yōu)化解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05vue實(shí)現(xiàn)todolist基本功能以及數(shù)據(jù)存儲(chǔ)功能實(shí)例詳解
本文通過(guò)實(shí)例代碼給大家介紹了vue實(shí)現(xiàn)todolist基本功能以及數(shù)據(jù)存儲(chǔ)功能,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04v-slot和slot、slot-scope之間相互替換實(shí)例
這篇文章主要介紹了v-slot和slot、slot-scope之間相互替換實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09Vue中子組件不能修改父組件傳來(lái)的Prop值的原因分析
在Vue中,父子組件之間通過(guò)Prop和Event實(shí)現(xiàn)了數(shù)據(jù)的雙向綁定,但是,Vue設(shè)計(jì)者為什么不允許子組件修改父組件傳遞的Prop呢,本文就來(lái)帶大家探究為什么子組件不能修改Prop,需要的朋友可以參考下2023-06-06vue報(bào)錯(cuò)Failed to execute 'appendChild&apos
這篇文章主要為大家介紹了vue報(bào)錯(cuò)Failed to execute 'appendChild' on 'Node'解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Vue倒計(jì)時(shí)3秒后返回首頁(yè)Demo(推薦)
這篇文章主要介紹了Vue倒計(jì)時(shí)3秒后返回首頁(yè)Demo,倒計(jì)時(shí)結(jié)束后要清除計(jì)時(shí)器,防止內(nèi)存泄漏,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-11-11vue3數(shù)據(jù)監(jiān)聽(tīng)watch/watchEffect的示例代碼
我們都知道監(jiān)聽(tīng)器的作用是在每次響應(yīng)式狀態(tài)發(fā)生變化時(shí)觸發(fā),在組合式?API?中,我們可以使用?watch()函數(shù)和watchEffect()函數(shù),下面我們來(lái)看下vue3如何進(jìn)行數(shù)據(jù)監(jiān)聽(tīng)watch/watchEffect,感興趣的朋友一起看看吧2023-02-02