Vue大屏展示列表向上循环滚动动画,requestAnimationFrame

一、背景:

<math xmlns="http://www.w3.org/1998/Math/MathML"> 同事忙飞了,叫我帮忙写个大屏列表滚动效果 \color {#000eac} {同事忙飞了,叫我帮忙写个大屏列表滚动效果} </math>同事忙飞了,叫我帮忙写个大屏列表滚动效果 ,正好我也有点空,就去了解下requestAnimationFrame的原理,简单学了下,写了一个案例给他用...

二、直接上完整代码:

js 复制代码
<template>
  <div
    ref="scroll"
    class="scroll"
    @mouseover="handleMouseOver"
    @mouseout="handleMouseOut"
  >
    <div
      v-for="item in list"
      ref="scrollItem"
      :key="item.id"
      class="scroll-item"
    >
      {{ item.title }}
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        {
          title: "滚动条1",
          id: 1
        },
        {
          title: "滚动条2",
          id: 2
        },
        {
          title: "滚动条3",
          id: 3
        },
        {
          title: "滚动条4",
          id: 4
        },
        {
          title: "滚动条5",
          id: 5
        },
        {
          title: "滚动条6",
          id: 6
        },
        {
          title: "滚动条7",
          id: 7
        },
        {
          title: "滚动条8",
          id: 8
        },
        {
          title: "滚动条9",
          id: 9
        },
        {
          title: "滚动条10",
          id: 10
        }
        // {
        //   title: "滚动条11",
        //   id: 11
        // },
        // {
        //   title: "滚动条12",
        //   id: 12
        // },
        // {
        //   title: "滚动条13",
        //   id: 13
        // },
        // {
        //   title: "滚动条14",
        //   id: 14
        // }
      ],
      loop: true, // 是否循环滚动
      autoplay: true, // 是否开启自动播放
      delay: 2000, // 停顿时间
      animationId: [], // 动画id列表
      isPlay: false // 是否正在播放
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    init() {
      if (this.autoplay) {
        this.scrollStart()
      }
    },
    reInit() {
      this.scrollStop()
      const scroll = this.$refs.scroll
      scroll.scrollTop = 0
      this.init()
    },
    scrollInfo() {
      const scroll = this.$refs.scroll
      const scrollItem = this.$refs.scrollItem
      const scrollHeight = scroll.offsetHeight
      const scrollItemHeight = scrollItem[0].offsetHeight
      return {
        scroll,
        scrollHeight,
        scrollItemHeight
      }
    },
    scrollStart() {
      const { scrollHeight, scrollItemHeight } = this.scrollInfo()
      const scrillContentHeight = scrollItemHeight * this.list.length
      if (scrillContentHeight < scrollHeight) return
      this.isPlay = true
      this.scrollEvent()
    },
    scrollStop() {
      this.isPlay = false
      this.animationId.map(v => cancelAnimationFrame(v))
    },
    scrollEvent() {
      if ("requestAnimationFrame" in window) {
        let id = requestAnimationFrame(this.animationFun)
        this.animationId.push(id)
      }
    },
    handleMouseOver() {
      this.scrollStop()
    },
    handleMouseOut() {
      this.scrollStart()
    },

    // 滚动 中间停顿
    animationFun(timeStamp, preTimeStamp = 0, diff = 0) {
      if (!this.isPlay) return

      const { scrollHeight, scrollItemHeight } = this.scrollInfo()
      const clientHeight = scrollItemHeight * this.list.length
      const scroll = this.$refs.scroll
      let currentDiff = timeStamp - preTimeStamp
      let n_diff = currentDiff + diff

      const stay = scroll.scrollTop % scrollItemHeight == 0

      // 中间停顿delay秒
      if (stay && n_diff < this.delay) {
        let id = requestAnimationFrame(_timeStamp =>
          this.animationFun(_timeStamp, timeStamp, n_diff)
        )
        this.animationId.push(id)
        return
      }
      // 循环滚动
      if (stay && this.loop) {
        this.loopAnimationFun()
      }

      if (scrollHeight + scroll.scrollTop < clientHeight) {
        scroll.scrollTop =
          scroll.scrollTop + this.findMinFactor(scrollItemHeight)
        let id = requestAnimationFrame(_timeStamp => {
          this.animationFun(_timeStamp, timeStamp, 0)
        })
        this.animationId.push(id)
      }
    },

    // 循环滚动
    loopAnimationFun() {
      if (!this.isPlay) return
      const scroll = this.$refs.scroll
      const { scrollItemHeight } = this.scrollInfo()
      const count = Math.floor(scroll.scrollTop / scrollItemHeight)
      scroll.scrollTop = Math.floor(scroll.scrollTop / scrollItemHeight) - count
      let newList = this.list.splice(0, count)
      this.list = this.list.concat(newList)
    },
    findMaxFactor(num) {
      if (num <= 1) return num
      for (let i = Math.floor(num / 2); i >= 1; i--) {
        if (num % i === 0) {
          return i
        }
      }
      return 1
    },
    findMinFactor(num) {
      if (num <= 1) return num
      for (let i = 2; i <= Math.floor(num / 2); i++) {
        if (num % i === 0) {
          return i
        }
      }
      return 1
    }
  }
}
</script>
<style lang="less" scoped>
.scroll {
  margin: 50px auto;
  width: 300px;
  height: 300px;
  overflow-y: hidden;
  &-item {
    height: 45px;
    line-height: 45px;
    background-color: #287e35;
    cursor: pointer;
  }
}
</style>

三、展示效果

四、总结

RequestAnimationFrame 是 JavaScript 的一个高级功能函数,用于优化网页应用的性能和用户体验。以下是它的几个主要优势:

  • 减少阻塞RequestAnimationFrame 会暂停当前脚本主线程,让其他操作可以在后台执行,从而避免主线程被卡死或延迟响应。
  • 支持 Web Workers 和 JavaScript 组件 :在处理复杂任务(如动画、数据渲染等)时,RequestAnimationFrame 可以帮助将这些任务分离开到独立的 workers 或渲染队列中,提升整体性能。
  • 处理大规模操作 :对于需要频繁更新 UI 的场景(如游戏、大数据可视化或实时数据展示),RequestAnimationFrame 能够高效地管理动画和渲染流程,避免卡顿。
  • 确保及时渲染 :在图形密集的场景中,RequestAnimationFrame 可以帮助保证每帧动画都能及时渲染,提升用户体验。
  • 支持渐变动画 :通过 RequestAnimationFrame,开发者可以实现更流畅、更复杂的动画效果,尤其是在结合 CSS 动画和 JavaScript 动态更新时。
  • 跨浏览器兼容性好 :无论是在 PC、移动端还是其他设备上,RequestAnimationFrame 都能稳定运行,帮助提升应用的通用性和可靠性。
相关推荐
程序员黄同学7 小时前
请谈谈 Vue 中的响应式原理,如何实现?
前端·javascript·vue.js
宁波阿成8 小时前
vue3里组件的v-model:value与v-model的区别
前端·javascript·vue.js
Jay丶萧邦8 小时前
el-select:有关多选,options选项值不包含绑定值的回显问题
javascript·vue.js·elementui
我爱学习_zwj9 小时前
后台管理系统-月卡管理
javascript·vue.js·elementui
customer0810 小时前
【开源免费】基于SpringBoot+Vue.JS个人博客系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
乐多_L10 小时前
使用vue3框架vue-next-admin导出表格excel(带图片)
前端·javascript·vue.js
初尘屿风11 小时前
基于微信小程序的电影院订票选座系统的设计与实现,SSM+Vue+毕业论文+开题报告+任务书+指导搭建视频
vue.js·微信小程序·小程序
customer0811 小时前
【开源免费】基于SpringBoot+Vue.JS打卡健康评测系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
合法的咸鱼11 小时前
uniapp 使用unplugin-auto-import 后, vue文件报红问题
前端·vue.js·uni-app
胡桃不是夹子12 小时前
vue登陆下拉菜单
前端·javascript·vue.js