Vue3文字滚动(TextScroll)

news2024/11/28 0:58:02

可自定义设置以下属性:

  • 滚动文字数组(sliderText),类型:Array<{title: string, link?: string}>,必传,默认[]

  • 滚动区域宽度(width),类型:number | string,默认 '100%'

  • 滚动区域高度(height),类型:number,单位px,默认 60

  • 滚动区域背景色(backgroundColor),类型:string,默认 '#FFF'

  • 滚动区域展示条数,水平滚动时生效(amount),类型:number,默认 4

  • 水平滚动文字各列间距或垂直滚动文字两边的边距(gap),类型:number,单位px,默认 20

  • 是否垂直滚动(vertical),类型:boolean,默认 false

  • 文字滚动时间间隔,垂直滚动时生效(interval),类型:number,单位ms,默认 3000

效果如下图: 

动画效果均使用requestAnimationFrame方法

①创建文字滚动组件TextScroll.vue:

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { requestAnimationFrame, cancelAnimationFrame, rafTimeout, cancelRaf } from '../index'
interface Text {
  title: string // 文字标题
  link?: string // 跳转链接
}
const props = defineProps({
  sliderText: { // 滚动文字数组
    type: Array<Text>,
    required: true,
    default: () => []
  },
  width: { // 滚动区域宽度
    type: [Number, String],
    default: '100%'
  },
  height: { // 滚动区域高度
    type: Number,
    default: 60
  },
  backgroundColor: { // 滚动区域背景色
    type: String,
    default: '#FFF'
  },
  amount: { // 滚动区域展示条数,水平滚动时生效
    type: Number,
    default: 4
  },
  gap: { // 水平滚动文字各列间距或垂直滚动文字两边的边距
    type: Number,
    default: 20
  },
  vertical: { // 是否垂直滚动
    type: Boolean,
    default: false
  },
  interval: { // 文字滚动时间间隔,垂直滚动时生效
    type: Number,
    default: 3000
  }
})
// horizon
const left = ref(0)
const fpsRaf = ref(0) // fps回调标识
const moveRaf = ref() // 一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义
const fps = ref(60)
const textData = ref<Text[]>([...props.sliderText])
const horizonRef = ref()
const distance = ref(0) // 每条滚动文字移动距离

const step = computed(() => { // 移动参数(120fps: 0.5, 60fps: 1)
  if (fps.value === 60) {
    return 1
  } else {
    return 60 / fps.value
  }
})
function getFPS () { // 获取屏幕刷新率
  // @ts-ignore
  const requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
  var start: any = null
  function timeElapse (timestamp: number) {
    /*
      timestamp参数:与performance.now()的返回值相同,它表示requestAnimationFrame() 开始去执行回调函数的时刻
    */
    if (!start) {
      if (fpsRaf.value > 10) {
        start = timestamp
      }
      fpsRaf.value = requestAnimationFrame(timeElapse)
    } else {
      fps.value = Math.floor(1000 / (timestamp - start))
      console.log('fps', fps.value)
      distance.value = getDistance() // 获取每列文字宽度
      onStart() // 开始滚动
    }
  }
  fpsRaf.value = requestAnimationFrame(timeElapse)
}
function getDistance ():number {
  return parseFloat((horizonRef.value.offsetWidth / props.amount).toFixed(2))
}
function moveLeft () {
  if (left.value >= distance.value) {
    textData.value.push(textData.value.shift() as Text) // 将第一条数据放到最后
    left.value = 0
  } else {
    left.value += step.value // 每次移动step(px)
  }
  moveRaf.value = requestAnimationFrame(moveLeft)
}

const totalWidth = computed(() => { // 文字滚动区域总宽度
  if (typeof props.width === 'number') {
    return props.width + 'px'
  } else {
    return props.width
  }
})
const len = computed(() => {
  return props.sliderText.length
})
onMounted(() => {
  if (props.vertical) {
    onStart() // 启动垂直滚动
  } else {
    getFPS()
  }
})
function onStart () {
  if (props.vertical) {
    if (len.value > 1) {
      startMove() // 开始滚动
    }
  } else {
    if (textData.value.length > props.amount) { // 超过amount条开始滚动
      moveRaf.value = requestAnimationFrame(moveLeft) // 开始动画
    }
  }
}
function onStop () {
  if (props.vertical) {
    if (len.value > 1) {
      cancelRaf(timer)
    }
  } else {
    cancelAnimationFrame(moveRaf.value) // 暂停动画
  }
}
const emit = defineEmits(['click'])
function onClick (title: string) { // 通知父组件点击的标题
  emit('click', title)
}

// vertical
const actIndex = ref(0)
var timer: any = null

function startMove () {
  timer = rafTimeout(() => {
    if (actIndex.value === len.value - 1) {
      actIndex.value = 0
    } else {
      actIndex.value++
    }
    startMove()
  }, props.interval)
}
</script>
<template>
  <div v-if="!vertical" class="m-slider-horizon" @mouseenter="onStop" @mouseleave="onStart" ref="horizonRef" :style="`height: ${height}px; width: ${totalWidth}; background: ${backgroundColor};`">
    <a
      :style="`will-change: transform; transform: translateX(${-left}px); width: ${distance - gap}px; margin-left: ${gap}px;`"
      class="u-slide-title"
      v-for="(text, index) in textData"
      :key="index"
      :title="text.title"
      :href="text.link ? text.link:'javascript:;'"
      :target="text.link ? '_blank':'_self'"
      @click="onClick(text.title)">
      {{ text.title || '--' }}
    </a>
  </div>
  <div v-else class="m-slider-vertical" @mouseenter="onStop" @mouseleave="onStart" :style="`height: ${height}px; width: ${totalWidth}; background: ${backgroundColor};`">
    <TransitionGroup name="slide">
      <div
        class="m-slider"
        :style="`width: calc(${totalWidth} - ${2*gap}px); height: ${height}px;`"
        v-for="(text, index) in sliderText"
        :key="index"
        v-show="actIndex===index">
        <a
          class="u-slider"
          :title="text.title"
          :href="text.link ? text.link:'javascript:;'"
          :target="text.link ? '_blank':'_self'"
          @click="onClick(text.title)">
        {{ text.title }}
        </a>
      </div>
    </TransitionGroup>
  </div>
</template>
<style lang="less" scoped>
// 水平滚动
.m-slider-horizon {
  box-shadow: 0px 0px 5px #D3D3D3;
  border-radius: 6px;
  white-space: nowrap;
  overflow: hidden;
  text-align: center; // 水平居中
  &:after { // 垂直居中
    content: '';
    height: 100%;
    display: inline-block;
    vertical-align: middle;
  }
  .u-slide-title {
    display: inline-block;
    vertical-align: middle;
    font-size: 16px;
    color: #333;
    font-weight: 400;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
    &:hover {
      color: @themeColor;
    }
  }
}

// 垂直滚动
.slide-enter-active, .slide-leave-active {
  transition: all 1s ease;
}
.slide-enter-from {
  transform: translateY(50px) scale(0.6);
  opacity: 0;
}
.slide-leave-to {
  transform: translateY(-50px) scale(0.6);
  opacity: 0;
}
.m-slider-vertical {
  position: relative;
  overflow: hidden;
  border-radius: 6px;
  .m-slider {
    position: absolute;
    left: 0;
    right: 0;
    margin: 0 auto;
    text-align: center; // 水平居中
    &:after { // 垂直居中
      content: '';
      height: 100%;
      display: inline-block;
      vertical-align: middle;
    }
    .u-slider {
      max-width: 100%;
      display: inline-block;
      vertical-align: middle;
      font-size: 18px;
      line-height: 28px;
      color: #333;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      cursor: pointer;
      &:hover {
        color: @themeColor;
      }
    }
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import { TextScroll } from './TextScroll.vue'
import { ref } from 'vue'
const sliderText = ref([
      {
        title: '美国作家杰罗姆·大卫·塞林格创作的唯一一部长篇小说',
        link: 'https://www.baidu.com'
      },
      {
        title: '首次出版于1951年'
      },
      {
        title: '塞林格将故事的起止局限于16岁的中学生霍尔顿·考尔菲德从离开学校到纽约游荡的三天时间内,塞林格将故事的起止局限于16岁的中学生霍尔顿·考尔菲德从离开学校到纽约游荡的三天时间内'
      },
      {
        title: '并借鉴了意识流天马行空的写作方法,充分探索了一个十几岁少年的内心世界'
      },
      {
        title: '愤怒与焦虑是此书的两大主题,主人公的经历和思想在青少年中引起强烈共鸣'
      }
    ])
function onClick (value: string) { // 获取点击的标题
  console.log('value:', value)
}
</script>
<template>
  <div>
    <h2 class="mb10">TextScroll 横向文字滚动基本使用</h2>
    <TextScroll
      :sliderText="sliderText"
      @click="onClick"
      width="100%"
      :amount="4"
      backgroundColor="#FFF"
      :height="50" />
    <h2 class="mt30 mb10">垂直文字滚动基本使用 (vertical)</h2>
    <TextScroll
      :sliderText="sliderText"
      @click="onClick"
      vertical
      backgroundColor="#e6f4ff"
      :gap="60"
      :interval="3000"
      width="100%"
      :height="60" />
  </div>
</template>
<style lang="less" scoped>
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428753.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能汽车迈进中央计算“芯”时代

全新一代的中央计算E/E架构正在加速到来。《高工智能汽车研究院》认为&#xff0c;伴随着整车电子电气架构从分布式架构、域控制器架构到中央计算架构演变&#xff0c;整个智能汽车供应链体系即将发生重构。 其中&#xff0c;车规芯片是至关重要的一环&#xff0c;从底层支撑着…

海创高科及时充系列共享充电宝 打开共享充电宝行业新局面

---让行业更加透明&#xff0c;更加真诚 在当下&#xff0c;电子产品很普遍。在使用电子产品的时候&#xff0c;需要进行充电。因此&#xff0c;各种充电宝有很大的市场需求。有些创业者感兴趣&#xff0c;那么&#xff0c;充电宝加盟哪个牌子好些&#xff1f;我们推荐海创高科…

【C语言】volatile关键字

文章目录一. 基本介绍二. 演示实验三. const 能否和 volatile 一起使用一. 基本介绍 volatile用来修饰变量&#xff0c;告知编译器不要对这个变量进行优化&#xff0c;每次要用到这个变量时都必须从内存中读取它的值。 看下面一段代码&#xff1a; 在 C/C 中被 const 修饰的…

激活函数(Activation Function)

目录 1 激活函数的概念和作用 1.1 激活函数的概念 1.2 激活函数的作用 1.3 通俗地理解一下激活函数&#xff08;图文结合&#xff09; 1.3.1 无激活函数的神经网络 1.3.2 带激活函数的神经网络 2 神经网络梯度消失与梯度爆炸 2.1 简介梯度消失与梯度爆炸 2.2 梯度不稳…

Java基础——缓冲流

&#xff08;1&#xff09;缓冲流概述&#xff1a; 缓冲流也称高效流&#xff0c;或者高级流。&#xff08;字节流可称原始流&#xff09;作用&#xff1a;缓冲流自带缓冲区&#xff0c;可以提高原始字节流&#xff0c;字符流读写数据的性能。&#xff08;2&#xff09;字节缓…

水质站房式在线监测系统集方案要点

水质在线自动监测系统是一套高度集成的一体化水质自动监测系统&#xff0c;其中包含水样采集处理、水质自动分析、数据采集传输、远程操作监控于一体的在线全自动监控系统。 本次方案整体系统采用一体化集成方式&#xff0c;辅助设备工艺制作精细&#xff0c;同时系统工艺流程…

阿木动态 | 阿木实验室亮相第58·59届中国高等教育展(重庆)

4月10日&#xff0c;第58.59届中国高等教育博览会在重庆国际博览中心圆满结束。本届展会中&#xff0c;各大科技企业、研发机构和教育组织都集中展示了最新的教育科技成果和应用。 阿木实验室作为一家注重提高客户研发效率的企业&#xff0c;此次展会中&#xff0c;阿木实验室 …

MinIO安装配置访问以及SpringBoot整合MinIO

MinIO 1.MinIO安装 Minio 是个基于 Golang 编写的开源对象存储服务&#xff0c;存储非结构化数据&#xff0c;如&#xff1a;图片&#xff0c;视频&#xff0c;音乐等 官网地址&#xff1a;https://min.io/ 中文地址&#xff1a;http://minio.org.cn 官网文档&#xff08; …

行业首选|墨菲安全实力入选《开发安全产品及服务购买决策参考》

十分钟快速部署&#xff0c;为企业提供最高效可靠的软件供应链安全解决方案&#xff01; 近日&#xff0c;GoUpSec 深入调研了14家国内开发安全“酷厂商”&#xff08;包括专业厂商和综合安全厂商&#xff09;&#xff0c;从产品功能、应用行业、成功案例、安全策略等维度对各厂…

DAY 41 WEB和HTTP协议

HTML概述 HTML叫做超文本标记语言&#xff0c;是一种规范&#xff0c;也是一种标准&#xff0c;它通过标记符号来标记要显示的网页中的各个部分。网页文件本身是一种文本文件&#xff0c;通过在文本文件中添加标记符&#xff0c;可以告诉浏览器如何显示其中的内容。 HTML命令可…

Android 纯应用的业务需求越来越窄,可否转向“智能座舱”?

回想2012年—2018年可谓是中国移动互联网的黄金时代&#xff0c;应用市场几乎每天都有新的App应用上线。 而如今却发生了巨大的改变&#xff0c;纯应用的业务需求越来越窄&#xff0c;岗位也随之收缩&#xff0c;这是作为Android开发者不容略的事实。 造成这种现象的原因是互…

类模板的三种表达方式

一:所有的类模板函数写在类的内部 template <typename T> class A { public:A(T a0) {this->a a;}T& getA() {return this->a;}A operator(const A& other) {A temp;//要求A的构造函数要有默认值temp.a this->a other.a;return temp;} private:T a;…

Java语言请求示例,电商商品详情接口,关键词搜索接口,代码封装

Java是一种编程语言&#xff0c;被特意设计用于互联网的分布式环境。Java具有类似于C语言的“形式和感觉”&#xff0c;但它要比C语言更易于使用&#xff0c;而且在编程时彻底采用了一种“以对象为导向”的方式。 使用Java编写的应用程序&#xff0c;既可以在一台单独的电脑上…

红十字救护员急救知识培训笔记

文章目录1、线上学习&#xff1a;理论知识2、线下培训&#xff1a;理论考试3、线下培训&#xff1a;实操学习实操考试3.1 心肺复苏CPR3.2 气道异物梗阻3.3 创伤救护一共有三个步骤 1、线上学习理论课程&#xff0c;所有课程进度100%可以报名线下 2、线下有一个实操的培训&#…

【实战经验】Android性能优化大分析:多种方法让你的应用飞起来

概述 Android性能优化是为了提高应用的响应速度、稳定性和用户体验。在应用开发中&#xff0c;当应用出现卡顿、卡死、运行缓慢等问题时&#xff0c;会给用户带来极差的体验&#xff0c;甚至导致用户流失。而进行性能优化可以有效地解决这些问题&#xff0c;提高应用的质量和用…

TryHackMe-Ra(windows)

Ra 您已经找到了WindCorp的内部网络及其域控制器。你能打开他们的网络吗&#xff1f; 您已经获得了WindCorp的内部网络的访问权限&#xff0c;这家价值数十亿美元的公司正在运行广泛的社交媒体活动&#xff0c;声称自己是不可破解的&#xff08;哈&#xff01;这个说法太多了…

OpenAI Translator Bob Plugin Bob上一款翻译、润色、语法修改插件

OpenAI Translator Bob Plugin 一款可以在Bob进行即时翻译的插件。它基于 OpenAI 的 GPT 系列模型&#xff0c;能够提供高质量、准确的翻译服务。该插件支持多种语言的翻译&#xff0c;包括中文、英文、法语、德语、日语等。用户只需在页面中选中需要翻译的文本&#xff0c;然…

MyBatisPlus3.4.3版自动生成代码的使用

AutoGenerator 是 MyBatis-Plus 的代码生成器&#xff0c;通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码&#xff0c;极大的提升了开发效率。 1 准备工作 创建springboot工程&#xff0c;这里省略。 2 导入依赖 <d…

Ae:灯光图层

灯光 Light是合成中可用于照亮其他 3D 图层并能产生投影、反射的一种元素&#xff0c;类似于光源。如果没有建立灯光图层&#xff0c;系统会使用默认的“环境光”来照亮场景。当建立了灯光图层之后&#xff0c;以所建立的灯光来照明。一般可根据实际需要创建一个或多个相同或不…

2023测试工程师全新技术栈,吃透这些,起薪就15k

相信每个准备软件测试面试的同学&#xff0c;不管你是大学刚毕业&#xff0c;满心憧憬着进入公司实习、非计算机行业转行软件测试、自学测试就业还是培训后就业&#xff0c;都会面临着众多的疑问和不解&#xff0c;那就是该怎么走出着第一步&#xff0c;今天本文一次性告诉你&a…