Vue实现无限轮播效果时动态绑定style失效的解决方法

 更新时间:2024年08月06日 08:30:07   作者:尖椒土豆sss  
最近在开发中遇到了一个新需求:列表轮播滚动,实现方式也有很多,比如使用第三方插件,但是由于不想依赖第三方插件,想自己实现,于是我开始了尝试,但是在这个过程中遇到了动态绑定style样式不生效,所以本文介绍了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失效的资料请关注脚本之家其它相关文章!

相关文章

  • element-ui循环显示radio控件信息的方法

    element-ui循环显示radio控件信息的方法

    今天小编就为大家分享一篇element-ui循环显示radio控件信息的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • vue实现的树形结构加多选框示例

    vue实现的树形结构加多选框示例

    这篇文章主要介绍了vue实现的树形结构加多选框,结合实例形式分析了在之前递归组件实现vue树形结构的基础之上再加上多选框功能相关操作技巧,需要的朋友可以参考下
    2019-02-02
  • Vue2.5通过json文件读取数据的方法

    Vue2.5通过json文件读取数据的方法

    本文通过实例代码给大家详细介绍了Vue2.5通过json文件读取数据的方法,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2018-02-02
  • vue.js如何将echarts封装为组件一键使用详解

    vue.js如何将echarts封装为组件一键使用详解

    Echarts 、 Remodal和Pikaday是我们在开发后台管理类网站时常用的三个第三方组件,下面这篇文章主要给大家介绍了关于vue.js如何将echarts封装为组件一键使用的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
    2017-10-10
  • vue项目如何引入element ui、iview和echarts

    vue项目如何引入element ui、iview和echarts

    这篇文章主要介绍了vue项目如何引入element ui、iview和echarts,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • 在vant中使用时间选择器和popup弹出层的操作

    在vant中使用时间选择器和popup弹出层的操作

    这篇文章主要介绍了在vant中使用时间选择器和popup弹出层的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • vue-cli+iview项目打包上线之后图标不显示问题及解决方法

    vue-cli+iview项目打包上线之后图标不显示问题及解决方法

    这篇文章主要介绍了解决vue-cli+iview项目打包上线之后图标不显示问题,本文通过两种方法给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-10-10
  • vue移动端如何解决click事件延迟,封装tap等事件

    vue移动端如何解决click事件延迟,封装tap等事件

    这篇文章主要介绍了vue移动端如何解决click事件延迟,封装tap等事件,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Vue父组件如何获取子组件中的变量

    Vue父组件如何获取子组件中的变量

    这篇文章主要为大家详细介绍了Vue父组件如何获取子组件中的变量,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • Vue 实现html中根据类型显示内容

    Vue 实现html中根据类型显示内容

    今天小编大家分享一篇Vue 实现html中根据类型显示内容,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-10-10

最新评论