Vue实现无限轮播效果时动态绑定style失效的解决方法
前言
最近在开发中遇到了一个新需求:列表轮播滚动;这个需求其实是比较常见的,实现方式也有很多,比如使用第三方插件:vue-seamless-scroll
,但是由于不想依赖第三方插件,想自己实现,于是我开始了尝试,但是在这个过程中遇到了动态绑定style样式不生效、绑定值与渲染值不一致等问题。谨以此篇记录自己的学习过程,也希望可以帮助到其他同学。
基础代码如下
创建一个 scroll.vue
组件,内容如下:
- 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>
实现方式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 高度,这通常是单行的高度,用于后续计算滚动的位移。获取
data
数组中的第一个元素,这个元素将在滚动时移动到列表的底部第一个
setTimeout
延迟 500 毫秒后执行以下操作:
this.data.push(shift)
:将第一个元素添加到data
数组的末尾,模拟循环移动。tabDom.style.transition = "all .5s"
:设置过渡效果为 0.5 秒,表示 marginTop 的变化将有动画效果。tabDom.style.marginTop =
-${rowOffsetHeight}px`` :将tabDom
的上边距设置为负的行高度,造成看似向上滚动的效果。
第二个
setTimeout
延迟 1000 毫秒后执行以下操作:this.data.splice(0, 1)
:移除data
数组中的第一个元素,完成滚动。tabDom.style.transition = "all 0s ease 0s"
:取消过渡效果,确保下次滚动时没有延迟。tabDom.style.marginTop = "0"
:将 marginTop 重置为 0,以准备下一次滚动。
主要是通过不停的将第一条数据添加到数组末尾、然后删除当前第一条数据,同时添加删除动画样式来实现滚动效果。这种方式添加删除时会出现停顿卡顿的现象。
实现方式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
值。
然后调用 setScrollClass
方法传参:
- offsetHeight / 2是dom的一半高度
- speed 轮播速度
最后将返回值设置给全局变量 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}`; }
该函数主要职责:
- 使用
document.createElement("style")
方法创建一个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: [ "机构类", "区域类", "国家类", "证照文书类", "业务对象类", "人员类", "法律法规类型", "金融类" ] } }
需要注意的是这里需要将原数据拷贝成两份数据,我这里使用计算属性+ 扩展运算符实现 scrollList
方法返回双份数据。
开始优化
在上面方式1中的代码已经可以实现dom列表的无限滚动效果;但是可以看出来该代码有优化的空间。
- 动态插入css动画
- 动态创建标签插入style
修改基础代码
添加: 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,计算出需要滚动的高度。这里因为动画只需要滚动一半的内容,因为我们的数据复制了两份。计算动画时间
duration
: 根据offsetHeight
和speed
计算动画的持续时间,公式为:offsetHeight * 1000
:将高度转换为毫秒(假设speed
是以像素/秒为单位)Math.floor
:向下取整,确保持续时间是一个整数。
动画参数
animation
设置 CSS 动画的属性:
${duration}ms
:指定动画持续时间(毫秒)。listRowup
:应为 CSS 中定义的动画名称,表示动画的具体效果。linear
:表示动画的速度曲线是线性的。infinite
:表示动画将无限循环。normal
:表示动画的播放方向为正常方向(从开始到结束)。
遇到问题
当在 mounted
中执行 dynamicStyle
方法时发现动画没有生效,检查元素后发现确实成功绑定动画了,但是为什么不生效呢?
于是将结果值与dom元素打印出来做对比:
发现动态绑定的style
animation
的属性结果与预期(函数设置的style)绑定的值竟然不同! 我一脸懵逼,反复查看代码发现没有错误啊!
尝试解决
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>
经过查资料分析发现原来是因为 style
标签的 scoped
属性导致的动画样式的选择器不匹配
在 scoped
样式中生成的类名会自动生成唯一标识符,浏览器无法找到这些动画的定义,导致动画效果无法生效!
比较推荐的方法就是将动画样式定义在全局样式文件中,这样可以确保 @keyframes
能够被任何元素正确的引用使用。
以上就是Vue实现无限轮播效果时动态绑定style失效的解决方法的详细内容,更多关于Vue动态绑定style失效的资料请关注脚本之家其它相关文章!
相关文章
vue项目如何引入element ui、iview和echarts
这篇文章主要介绍了vue项目如何引入element ui、iview和echarts,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-09-09vue-cli+iview项目打包上线之后图标不显示问题及解决方法
这篇文章主要介绍了解决vue-cli+iview项目打包上线之后图标不显示问题,本文通过两种方法给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下2019-10-10
最新评论