實現效果
初級方法:
實現原理:
- 由於列表的總數是變化的,所以不能用css把動畫寫死,通過定時器移動列表,實現動畫效果
- 計算總高度,建一個變量存儲移動距離,兩者之前比較,當移動距離>=總高度 就把移動距離重置為0
- 在css中給列表盒子絕對定位,通過移動top值實現動畫
注意事項:由於需要循環滾動,可以把獲取到的數組在復制一份放到列表里,做到無縫銜接
html:
<div class="inner_box"> <div class="prize_container"> <ul ref="prizeRef" class="prize_ul" v-if="info.joinList && info.joinList.length" > <li class="prize_item" v-for="(item, index) in info.joinList" :key="index" > <div class="left">{{ item.mobile | mobileFilter }}</div> <div class="right">{{ item.name }}</div> </li> </ul> </div> </div>
js:
// 獲取活動信息 getData() { this.fetch .get("TjModule/GetActivityInfo", { activityId: this.id }) .then(res => { if (res.data && res.data.success) { this.$setTitle(res.data.result.activityName); this.info = res.data.result; let arr = []; if (res.data.result.joinList.length) { arr = res.data.result.joinList; arr.map(i => { arr.push(i); }); } this.info.joinList = arr; console.log(this.info.joinList); this.$nextTick(() => { this.changeAnimation(this.info.joinList.length); }); } }) .catch(error => { if (error.response && error.response.status === 500) { this.$toast(error.response.data.error.message); } }); }, // 編寫動畫 changeAnimation(d) { console.log(9090); let total = 25.5 * d/2 let every = 0 const dm = this.$refs.prizeRef; this.timers = setInterval(()=>{ every += 5 if(every < total){ dm.style.top = - every + 'px' }else{ dm.style.top = 0 every = 0 } },120) },
// 關閉定時器
beforeDestroy() {
clearInterval(this.timers)
},
css:
.prize_container { overflow: hidden; position: absolute; height: 90%; width: 100%; padding: 30px 0; } .prize_ul { width: 100%; color: #fff; position: absolute; padding: 0px 80px; // animation: opacityShow linear 5s infinite; .prize_item { width: 100%; display: flex; justify-content: space-between; margin-bottom: 12px; } }
在changeAnimation函數中, let total = 25.5 * d/2 這個25.5是每行的高度,通過 every(移動距離) 與 total(總高度) 比較,實現循環
缺點:
- 滾動的時候總是感覺一卡一卡的效果,不夠流暢( 原因: 瀏覽器每秒刷新的次數 和 每次移動距離的時間頻率 不一致,導致視覺上產生卡頓效果 )
- 通過top值進行移動,比較損耗性能,每次改變都會影響布局
- 獲取總高度total,不要手動計算,應該通過 clientHeight 動態獲取比較好
高級方法:
使用一個屬性: 幀動畫
window.requestAnimationFrame()
告訴瀏覽器——你希望執行一個動畫,並且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。該方法需要傳入一個回調函數作為參數,該回調函數會在瀏覽器下一次重繪之前執行
這個方法掛載在window上,很方便我們的使用
實現原理:
- 通過控制 transform 的 translateY值進行移動動畫
- 動態獲取列表高度,padding,margin去掉,否則會在計算高度的時候,有誤差,循環銜接點會有卡頓
- requestAnimationFrame與animate函數相互調用,執行幀動畫requestAnimationFrame時,調用移動動畫的函數,實現同頻率改變 requestAnimationFrame(this.animate)
animate() {
requestAnimationFrame(this.animate);
this.changeAnimation();
},
html:
<div class="prize_container"> <div class="prize_list" :style="{ transform: 'translateY( -'+ ulLeft + 'px)' }" > <ul ref="prizeUl" class="prize_ul" v-if="info.joinList && info.joinList.length" > <li class="prize_item" v-for="(item, index) in info.joinList" :key="index" > <div class="left">{{ item.mobile | mobileFilter }}</div> <div class="right">{{ item.name }}</div> </li> </ul> <ul class="prize_ul" v-if="info.joinList && info.joinList.length" > <li class="prize_item" v-for="(item, index) in info.joinList" :key="index" > <div class="left">{{ item.mobile | mobileFilter }}</div> <div class="right">{{ item.name }}</div> </li> </ul> </div> </div>
這里使用復制兩個ul元素,不在js中操作復制列表
js:
data(){ prizeUlHeight:0, //滾動ref高度 $prizeUl:null, //滾動的ref ulLeft:0, //移動參數 }
beforeDestroy() {
cancelAnimationFrame(this.times); //清理原生的監聽
},
methods:{ // 獲取活動信息 getData() { this.fetch .get("TjModule/GetActivityInfo", { activityId: this.id }) .then(res => { if (res.data && res.data.success) { this.$setTitle(res.data.result.activityName); this.info = res.data.result; this.$nextTick(() => { this.$prizeUl = this.$refs.prizeUl; this.prizeUlHeight = this.$prizeUl.clientHeight; this.times = requestAnimationFrame(this.animate); }); } }) .catch(error => { if (error.response && error.response.status === 500) { this.$toast(error.response.data.error.message); } }); }, animate() { requestAnimationFrame(this.animate); this.changeAnimation(); }, // 編寫動畫 changeAnimation(d) { if( this.ulLeft == this.prizeUlHeight ){ this.ulLeft = 0; }else{ this.ulLeft += 1; } }, }
css:
.prize_container { overflow: hidden; position: absolute; height: 90%; width: 100%; } .prize_ul { width: 100%; color: #fff; // position: absolute; padding: 0px 80px; .prize_item { width: 100%; display: flex; line-height: 58px; justify-content: space-between; } }
這樣就完美解決了循環滾動動畫,
使用幀動畫還有一個優點:
每次切換頁面的時候,幀動畫會暫停執行,直到切換回該頁面,才會繼續執行動畫,這樣就比定時器更加保護性能
其實還有一個方案:
用css + js
- css中寫動畫,把動畫屬性分開寫, animation-duration 動態計算:根據每行花費幾秒 * 幾行 這樣動態賦值
- css中通過控制 transform 的 translateY值進行移動50%即可
// 寫動畫 @keyframes opacityShow { from { transform: translateY(0); } to { transform: translateY(-50%); } }
.prize_ul {
width: 100%;
color: #fff;
padding: 0px 80px;
animation-name: opacityShow;
animation-iteration-count: infinite;
.prize_item {
width: 100%;
display: flex;
line-height: 58px;
justify-content: space-between;
}
}
使用