Kros的博客 Kros的博客
首页
  • CSS
  • 工具
  • Vue
  • js
  • Vue3
  • 算法
  • 折腾笔记
一言
  • 分类
  • 标签
  • 归档
码云

Kros

凡心所向,素履以往,生如逆旅,一苇以航
首页
  • CSS
  • 工具
  • Vue
  • js
  • Vue3
  • 算法
  • 折腾笔记
一言
  • 分类
  • 标签
  • 归档
码云
  • CSS

  • JavaScript

  • 工具

  • Vue

  • antdv踩坑记录

  • Vue3

    • vue3 setup语法糖
    • vue3实现v-model
    • vue3组件通信
    • vue3使用pinia及pinia持久化
    • vue3使用$nextTick
    • Proxy的实际应用场景
    • ref和reactive的实现原理
    • vue3生命周期
    • 在vue文件外使用store
    • vue3中的编译器宏
    • vue3 ts中拼接本地图片地址
    • 数值滚动实现
    • setup语法糖下自定义组件name
    • vue-router4配置动态路由问题No match found for location with path
    • vue3挂载全局弹窗
    • vue3+vite配置环境变量问题
    • Vue3监听createApp创建的子组件事件
    • vue3源码学习——vue3核心模块
  • 前端
  • Vue3
kros
2025-08-11

数值滚动实现

scroller

# 实现思路

将数值转换成字符串并拆分每个字符,将字符遍历渲染到页面。如果是非数值则直接显示,数值类型则在当前位置生成0-9的列,根据当前数值计算y方向的位置偏移 千分位显示:千分位下默认只保留两位小数,整数部分通过正则num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')将每3位数字添加分隔符,即可

# 实现代码

<template>
  <div class="roll-wrap" :style="{ fontSize: `${cellHeight}px` }">
    <ul class="roll-box">
      <slot name="prefix"></slot>
      <li
        class="roll-item"
        v-for="(item, index) in numberArr"
        :key="index"
        :style="{ height: `${cellHeight}px`, lineHeight: `${cellHeight}px` }"
      >
        <!--小数点或其他情况-->

        <div v-if="isNaN(parseFloat(item))" class="roll-num">{{ item }}</div>

        <div v-else :style="getStyles(index)">
          <!--数字0到9-->

          <div
            :style="{ height: `${cellHeight}px`, lineHeight: `${cellHeight}px` }"
            v-for="(subItem, subIndex) in oneToNineArr"
            :key="subIndex"
            class="roll-num"
          >
            {{ subItem }}
          </div>
        </div>
      </li>
      <slot name="suffix"></slot>
    </ul>
  </div>
</template>
<script lang="ts" setup>
import { ref, onBeforeMount, watch } from 'vue'

const numFormat = (num: string) => {
  const c = num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
  return c
}

interface PropsType {
  cellHeight?: number // 高度
  rollNumber?: string | number // 当前数值
  dur?: number // 动画持续时间
  easeFn?: string // 动画
  thousands?: boolean // 千分位 10000 => 10,000
}
const props = withDefaults(defineProps<PropsType>(), {
  cellHeight: 30,
  rollNumber: 0,
  dur: 1500,
  easeFn: 'ease',
  thousands: false
})

const number = ref()

const numberArr = ref<Array<string>>([])

const numberOffsetArr = ref<Array<number>>([])

const oneToNineArr = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

const resetState = (len: number) => {
  const newArr = new Array(len).join(',').split(',')
  numberOffsetArr.value = newArr.map(() => 0)
  // 延迟执行动画
  setTimeout(() => {
    // 设置传入的数字下标对应偏移量,重新赋值
    numberArr.value.forEach((num, i) => {
      numberOffsetArr.value[i] = parseInt(num) * props.cellHeight
      // this.$set(this.numberOffsetArr, i, num * this.cellHeight)
    })
  }, 30)
}

const getStyles = (index: number) => {
  const style = {
    transition: `${props.easeFn} ${props.dur}ms`,
    transform: `translate(0%, -${numberOffsetArr.value[index]}px)`
  }
  return style
}

// 监听props
watch(
  () => props.rollNumber,
  (value) => {
    // 格式化数字
    let ruleValue = value + ''
    if (ruleValue) {
      let valueArr = ruleValue.split('.')
      if(props.thousands) {
        ruleValue = numFormat(valueArr[0])
        if (valueArr[1]) {
         ruleValue += '.' + valueArr[1].substring(0, 2)
        }
      }      
    }
    number.value = `${ruleValue}`
    numberArr.value = `${ruleValue}`.split('')
    resetState(numberArr.value.length)
  },
  {
    immediate: true
  }
)

onBeforeMount(() => {
  number.value = props.rollNumber + ''
})
</script>

<style lang="less" scoped>
.roll-wrap {
  ul.roll-box {
    display: flex;
    padding: 0;
    margin: 0;
    text-align: center;
    overflow: hidden;
  }

  li {
    overflow: hidden;
    .roll-num {
      font-size: 32px;
      font-weight: 600;
      color: #fff;
      letter-spacing: 2px;
    }
  }
}
</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
上次更新: 2025/09/05, 8:09:00
vue3 ts中拼接本地图片地址
setup语法糖下自定义组件name

← vue3 ts中拼接本地图片地址 setup语法糖下自定义组件name→

最近更新
01
Find the next perfect square
09-05
02
Regex validate PIN code
09-05
03
Find the odd int
09-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 kros king
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
icon-heart-o icon-heart icon-infinity icon-pause icon-play link next prev