Vue實現(xiàn)無限輪播效果時動態(tài)綁定style失效的解決方法
前言
最近在開發(fā)中遇到了一個新需求:列表輪播滾動;這個需求其實是比較常見的,實現(xiàn)方式也有很多,比如使用第三方插件:vue-seamless-scroll,但是由于不想依賴第三方插件,想自己實現(xiàn),于是我開始了嘗試,但是在這個過程中遇到了動態(tài)綁定style樣式不生效、綁定值與渲染值不一致等問題。謹以此篇記錄自己的學(xué)習(xí)過程,也希望可以幫助到其他同學(xué)。
基礎(chǔ)代碼如下
創(chuàng)建一個 scroll.vue 組件,內(nèi)容如下:
- html
<div class="domScroll">
<div
:class="['scroll-content', rollClass]">
<slot></slot>
<slot></slot>
</div>
</div>
- js
export default {
data() {
return {
rollClass: ""
};
}
}
- css樣式
<style lang="less" scoped>
.domScroll {
overflow: hidden;
}
</style>
實現(xiàn)方式1(不推薦)
tableScroll() {
this.$nextTick(() => {
const tabDom = this.$el.querySelector(".scroll-content");
const rowOffsetHeight = this.$el.querySelector(".row-item")
.offsetHeight;;
setInterval(() => {
const shift = this.data[0];
setTimeout(() => {
this.data.push(shift);
tabDom.style.transition = "all .5s";
tabDom.style.marginTop = `-${rowOffsetHeight}px`;
}, 500);
setTimeout(() => {
this.data.splice(0, 1);
tabDom.style.transition = "all 0s ease 0s";
tabDom.style.marginTop = "0";
}, 1000);
}, 1500);
});
}
代碼分析
rowOffsetHeight:獲取列表 item 高度,這通常是單行的高度,用于后續(xù)計算滾動的位移。獲取
data數(shù)組中的第一個元素,這個元素將在滾動時移動到列表的底部第一個
setTimeout延遲 500 毫秒后執(zhí)行以下操作:
this.data.push(shift):將第一個元素添加到data數(shù)組的末尾,模擬循環(huán)移動。tabDom.style.transition = "all .5s":設(shè)置過渡效果為 0.5 秒,表示 marginTop 的變化將有動畫效果。tabDom.style.marginTop =-${rowOffsetHeight}px`` :將tabDom的上邊距設(shè)置為負的行高度,造成看似向上滾動的效果。
第二個
setTimeout延遲 1000 毫秒后執(zhí)行以下操作:this.data.splice(0, 1):移除data數(shù)組中的第一個元素,完成滾動。tabDom.style.transition = "all 0s ease 0s":取消過渡效果,確保下次滾動時沒有延遲。tabDom.style.marginTop = "0":將 marginTop 重置為 0,以準備下一次滾動。
主要是通過不停的將第一條數(shù)據(jù)添加到數(shù)組末尾、然后刪除當前第一條數(shù)據(jù),同時添加刪除動畫樣式來實現(xiàn)滾動效果。這種方式添加刪除時會出現(xiàn)停頓卡頓的現(xiàn)象。
實現(xiàn)方式2(推薦)
初始化方法
init() {
const scrollContent = this.$el.querySelector(".scroll-content");
if (scrollContent) {
const offsetHeight = scrollContent.offsetHeight;
const scrollClass = this.setScrollClass(offsetHeight / 2, this.speed);
this.rollClass = scrollClass;
}
},
首先通過 this.$el.querySelector(".scroll-content"); 獲取到目標dom元素,如果該dom存在,獲取其 offsetHeight值。
然后調(diào)用 setScrollClass方法傳參:
- offsetHeight / 2是dom的一半高度
- speed 輪播速度
最后將返回值設(shè)置給全局變量 rollClass
setScrollClass方法
setScrollClass(offsetHeight, speed) {
const uid = Math.random().toString(36).substring(2, 5);
const style = document.createElement("style");
style.innerHTML = `
@keyframes listRowup${uid} {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
-webkit-transform: translate3d(0, -50%, 0);
transform: translate3d(0, -50%, 0);
}
}
.rowup-${uid} {
-webkit-animation: ${Math.floor(
(offsetHeight * 1000) / speed
)}ms listRowup${uid} linear infinite normal;
animation: ${Math.floor(
(offsetHeight * 1000) / speed
)}ms listRowup${uid} linear infinite normal;
}
`;
document.getElementsByTagName("head")[0].appendChild(style);
return `rowup-${uid}`;
}
該函數(shù)主要職責(zé):
- 使用
document.createElement("style")方法創(chuàng)建一個style標簽并插入@keyframes動畫、class - 最終獲取
head標簽將生成的style標簽插入 - 返回生成的好的動畫
class類名
組件使用
在主文件引入 scroll.vue
- html
<Domscroll :height="140">
<div class="listBox flex">
<div
v-for="item in scrollList"
:key="item"
class="item"
>
{{ item }}
</div>
</div>
</Domscroll>
- js
computed: {
scrollList() {
return [...this.list1,...this.list1]
},
data() {
return {
list1: [
"機構(gòu)類",
"區(qū)域類",
"國家類",
"證照文書類",
"業(yè)務(wù)對象類",
"人員類",
"法律法規(guī)類型",
"金融類"
]
}
}
需要注意的是這里需要將原數(shù)據(jù)拷貝成兩份數(shù)據(jù),我這里使用計算屬性+ 擴展運算符實現(xiàn) scrollList 方法返回雙份數(shù)據(jù)。
開始優(yōu)化
在上面方式1中的代碼已經(jīng)可以實現(xiàn)dom列表的無限滾動效果;但是可以看出來該代碼有優(yōu)化的空間。
- 動態(tài)插入css動畫
- 動態(tài)創(chuàng)建標簽插入style
修改基礎(chǔ)代碼
添加: ref="scrollContent"
- html
<div class="domScroll">
<div
ref="scrollContent"
:class="['scroll-content']"
:style="scrollSty">
<slot></slot>
<slot></slot>
</div>
</div>
添加:scrollSty變量
- js
export default {
data() {
return {
scrollSty: {
animation: ""
}
};
}
}
將css動畫添加到css中
- css樣式
<style lang="less" scoped>
.domScroll {
overflow: hidden;
@keyframes rowup {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
100% {
-webkit-transform: translate(0, -50%);
transform: translate(0, -50%);
}
}
}
</style>
修改methods方法
dynamicStyle(speed) {
const scrollContent = this.$refs.scrollContent;
const offsetHeight = scrollContent.offsetHeight / 2;
const duration = Math.floor((offsetHeight * 1000) / speed);
this.scrollSty = {
animation: `${duration}ms listRowup linear infinite normal`,
"-webkit-animation": `${duration}ms listRowup linear infinite normal`
};
},
代碼分析
獲取dom
通過this.$refs.scrollContent直接獲取組件的 DOM 元素計算偏移高度
offsetHeight:獲取scrollContent元素的高度,并將其除以 2,計算出需要滾動的高度。這里因為動畫只需要滾動一半的內(nèi)容,因為我們的數(shù)據(jù)復(fù)制了兩份。計算動畫時間
duration: 根據(jù)offsetHeight和speed計算動畫的持續(xù)時間,公式為:offsetHeight * 1000:將高度轉(zhuǎn)換為毫秒(假設(shè)speed是以像素/秒為單位)Math.floor:向下取整,確保持續(xù)時間是一個整數(shù)。
動畫參數(shù)
animation設(shè)置 CSS 動畫的屬性:
${duration}ms:指定動畫持續(xù)時間(毫秒)。listRowup:應(yīng)為 CSS 中定義的動畫名稱,表示動畫的具體效果。linear:表示動畫的速度曲線是線性的。infinite:表示動畫將無限循環(huán)。normal:表示動畫的播放方向為正常方向(從開始到結(jié)束)。
遇到問題
當在 mounted 中執(zhí)行 dynamicStyle方法時發(fā)現(xiàn)動畫沒有生效,檢查元素后發(fā)現(xiàn)確實成功綁定動畫了,但是為什么不生效呢?
于是將結(jié)果值與dom元素打印出來做對比:

發(fā)現(xiàn)動態(tài)綁定的style animation的屬性結(jié)果與預(yù)期(函數(shù)設(shè)置的style)綁定的值竟然不同! 我一臉懵逼,反復(fù)查看代碼發(fā)現(xiàn)沒有錯誤?。?/p>
嘗試解決
mounted中使用$nextTick(function(){})方法無效修改@keyframes動畫名稱無效
最終解決
- 動畫css單獨寫一個style標簽,不添加scoped
<style lang="less" scoped>
.domScroll {
overflow: hidden;
}
</style>
<style>
@keyframes rowup {
0% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
100% {
-webkit-transform: translate(0, -50%);
transform: translate(0, -50%);
}
}
</style>
經(jīng)過查資料分析發(fā)現(xiàn)原來是因為 style 標簽的 scoped 屬性導(dǎo)致的動畫樣式的選擇器不匹配
在 scoped 樣式中生成的類名會自動生成唯一標識符,瀏覽器無法找到這些動畫的定義,導(dǎo)致動畫效果無法生效!
比較推薦的方法就是將動畫樣式定義在全局樣式文件中,這樣可以確保 @keyframes 能夠被任何元素正確的引用使用。
以上就是Vue實現(xiàn)無限輪播效果時動態(tài)綁定style失效的解決方法的詳細內(nèi)容,更多關(guān)于Vue動態(tài)綁定style失效的資料請關(guān)注腳本之家其它相關(guān)文章!

