当前位置:   article > 正文

Vue3文字实现左右和上下滚动_vue文字向上循环滚动

vue文字向上循环滚动

可自定义设置以下属性:

滚动文字数组(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

详见:描述

1:创建文字滚动组件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 // 跳转链接
}
interface Props {
  sliderText: Text[] // 滚动文字数组
  width?: number|string // 滚动区域宽度,单位px
  height?: number // 滚动区域高度,单位px
  backgroundColor?: string // 滚动区域背景色
  amount?: number // 滚动区域展示条数,水平滚动时生效
  gap?: number // 水平滚动文字各列间距或垂直滚动文字两边的边距,单位px
  vertical?: boolean // 是否垂直滚动
  interval?: number // 文字滚动时间间隔,单位ms,垂直滚动时生效
}
const props = withDefaults(defineProps<Props>(), {
  sliderText: () => [],
  width: '100%',
  height: 60,
  backgroundColor:  '#FFF',
  amount: 4,
  gap: 20,
  vertical: false,
  interval: 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245

2:在要使用的页面引入

<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号