【vue】NoticeBar:滚动通知栏组件手动实现(内容、速度、循环间隔可配置)

文章目录

背景

之前做了一个需求,里面有一个滚动通知栏

由于是活动页面,逻辑不复杂,使用技术栈为svelte,没有UI组件库。原生实现的通知栏,它的滚动有点问题。由于时间紧张,使用了不够好的方法实现:
【IOS webview】css动画,首次进入异常(滚动很缓慢),息屏后重新进入才正常_css3 animation scroll动画在ios中需要切一下屏幕才会运行-CSDN博客

目的:手动实现一个滚动通知栏。了解循环滚动逻辑。

参考: NoticeBar 通知栏 - Vant4

效果

html 复制代码
<NoticeBarVue :content="content" :speed="speed" :loopWait="loopWait"></NoticeBarVue>

可配置:

  • content:内容,一般为长文本。需要大于容器距离才会滚动
  • speed:移动速度。每秒移动多少px
  • loopWait:循环间隔。循环完成后等待多少秒后进行下一次循环。

由于容器的宽度、背景色等配置不是本次代码的重点,直接写死了。如有需要可以自己改为可配置的。

逻辑

初始状态下,内容位置:

滚动逻辑:

代码

初始化

html 复制代码
  <div class="scroll-bar" ref="scrollBarRef">
    <div class="content" ref="contentRef">{{ props.content }}</div>
  </div>

获取容器宽度(offsetWidth)和内容实际宽度(scrollWidth )。

typescript 复制代码
  containerWidth.value = scrollBarRef.value.offsetWidth
  contentWidth.value = contentRef.value.scrollWidth // scrollWidth 内容的实际宽度

滚动逻辑

position 表示当前内容偏移位置:

  • 0:相当于初始状态
  • -contentWidth:向左移动距离为内容距离,即向左滚动完毕的距离
  • containerWidth:向右距离为容器距离,即循环刚开始的距离
  • containerWidth.value + props.loopWait * props.speed:加上了循环间隔的距离。
    • 如果不想循环完成后立马开始循环,可以增加一个等待时间。这里定义为loopWait
    • loopWait*speed 就是多走的距离,即循环间隔的距离
typescript 复制代码
const handleScroll = () => {
  if (!scrollBarRef.value || !contentRef.value) {
    return
  }

  //   滚动完时,重置位置。循环的开始位置是容器最右边
  if (position.value < -contentWidth.value) {
    position.value = containerWidth.value + props.loopWait * props.speed
  }

  position.value -= props.speed // 速度:每秒向左移动的距离

  contentRef.value.style.transform = `translateX(${position.value}px)`
  //   平滑滚动。告诉浏览器,在下次重绘之前,调用此回调
  scrollId.value = requestAnimationFrame(() => handleScroll())
}

设置contentRef的偏移距离。

调用requestAnimationFrame:效果是很平滑地滚动。

告诉浏览器你希望执行一个动画。它要求浏览器在下一次重绘之前,调用用户提供的回调函数。
Window:requestAnimationFrame() 方法 - Web API | MDN

完整代码

组件:

html 复制代码
<template>
  <div class="scroll-bar" ref="scrollBarRef">
    <div class="content" ref="contentRef">{{ props.content }}</div>
  </div>
</template>

<script setup lang="ts">
import { defineProps, ref, onMounted } from 'vue'
interface NOTICEBAR {
  content: string
  speed: number // 每秒移动距离
  loopWait: number // 循环间隔
}

const props = defineProps<NOTICEBAR>()
const scrollBarRef = ref()
const contentRef = ref()
const scrollId = ref()

onMounted(() => {
  initScroll()
})

const position = ref(0)
const containerWidth = ref(0)
const contentWidth = ref(0)

// 初始化滚动
const initScroll = () => {
  if (!scrollBarRef.value || !contentRef.value) {
    return
  }

  containerWidth.value = scrollBarRef.value.offsetWidth
  contentWidth.value = contentRef.value.scrollWidth // scrollWidth 内容的实际宽度

  // 容器>内容,无需滚动
  if (containerWidth.value > contentWidth.value) {
    contentRef.value.style.transform = 'translateX(0)'
    return
  }

  handleScroll()
}

const handleScroll = () => {
  if (!scrollBarRef.value || !contentRef.value) {
    return
  }

  //   滚动完时,重置位置。循环的开始位置是容器最右边
  if (position.value < -contentWidth.value) {
    position.value = containerWidth.value + props.loopWait * props.speed
  }

  position.value -= props.speed // 速度

  contentRef.value.style.transform = `translateX(${position.value}px)`
  //   平滑滚动。告诉浏览器,在下次重绘之前,调用此回调
  scrollId.value = requestAnimationFrame(() => handleScroll())
}
</script>

<style lang="less" scoped>
.scroll-bar {
  width: 400px;
  overflow: hidden;
}
</style>

调用:

html 复制代码
<template>
  <div>
    <a href="https://vant4.ylhtest.com/#/zh-CN/notice-bar">参考vant</a>

    <div class="container">
      <NoticeBarVue :content="content" :speed="speed" :loopWait="loopWait"></NoticeBarVue>
    </div>
  </div>
</template>

<script setup lang="ts">
import NoticeBarVue from '@/components/notice-bar.vue'

const content = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz1234567890'
const speed = 1
const loopWait = 50
</script>

<style lang="less" scoped>
.container {
  background-color: #fffbe8;
  margin: 10px 0;
}
</style>

心得

理解了滚动通知栏、循环滚动。

【IOS webview】css动画,首次进入异常(滚动很缓慢),息屏后重新进入才正常_css3 animation scroll动画在ios中需要切一下屏幕才会运行-CSDN博客的问题是,没有显示地计算宽度。所以页面自适应地渲染的时候,会出问题(默认为0,渲染出来后才有宽度)。

当时把宽度写死了,相当于替代了 动态计算 的步骤。

相关推荐
浪裡遊8 小时前
Next.js路由系统
开发语言·前端·javascript·react.js·node.js·js
mapbar_front8 小时前
职场中的顶级能力—服务意识
前端
STUPID MAN8 小时前
Linux使用tomcat发布vue打包的dist或html
linux·vue.js·tomcat·html
尽兴-9 小时前
[特殊字符] 微前端部署实战:Nginx 配置 HTTPS 与 CORS 跨域解决方案(示例版)
前端·nginx·https·跨域·cors·chrom
JIngJaneIL9 小时前
助农惠农服务平台|助农服务系统|基于SprinBoot+vue的助农服务系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·毕设·助农惠农服务平台
云外天ノ☼10 小时前
待办事项全栈实现:Vue3 + Node.js (Koa) + MySQL深度整合,构建生产级任务管理系统的技术实践
前端·数据库·vue.js·mysql·vue3·koa·jwt认证
gihigo199810 小时前
使用JavaScript和Node.js构建简单的RESTful API
javascript·node.js·restful
一位搞嵌入式的 genius10 小时前
前端实战开发(三):Vue+Pinia中三大核心问题解决方案!!!
前端·javascript·vue.js·前端实战
塞纳河畔的歌10 小时前
保姆级教程 | 麒麟系统安装Edge浏览器
前端·edge
前端.火鸡10 小时前
Vue 3.5 新API解析:响应式革命、SSR黑科技与开发体验飞跃
javascript·vue.js·科技