项目要做一个数字滚动的效果,网上搜索找到两篇文章你可能需要这样的大屏数字滚动效果和【vue自定义组件】实现大屏数字滚动翻转效果,研究下来角色效果不错,于是在两位大佬的基础上做了二次封装,一个细节需要注意下,v-for的key的定义,如果直接使用index可能会导致显示错乱,参考# vue中使用v-for时为什么要用到key?为什么不能用index作为key?
<template>
<div class="box-item">
<li
v-for="(item, index) in orderNum"
:key="item.toString() + index.toString() + getRandomNumber(1, 10000)"
:class="{ 'number-item': !isNaN(item), 'mark-item': isNaN(item) }"
>
<span v-if="!isNaN(item)">
<i ref="numberItem">0123456789</i>
</span>
<span v-else class="comma">{{ item }}</span>
</li>
</div>
</template>
<script>
export default {
name: 'FlipNumber',
props: {
// 具体数值
number: {
type: Number,
default() {
return 0
}
},
// 数字更新的时间
time: {
type: Number,
default() {
return 1
}
},
// 数字最小长度,长度不足时根据fillZero配置补0
numberLength: {
type: Number,
default: 8
},
// 不足numberLength时是否在前面补0
fillZero: {
type: Boolean,
default: false
}
},
data() {
return {
orderNum: []
}
},
watch: {
number(newVal, oldVal) {
this.orderNum = this.transNum2ShowValue(newVal) // 这里输入数字即可调用
this.flip(oldVal)
}
},
mounted() {
this.orderNum = this.transNum2ShowValue(this.number) // 这里输入数字即可调用
this.flip()
},
methods: {
flip(oldVal) {
const self = this
// orderNum更改之后,页面会重新渲染,即数字全部归0,如果不赋值一次旧,会出现全0的情况
this.$nextTick(() => {
// 重新渲染之后先给<i>赋旧值
if (oldVal) {
this.setNumberTransform(this.transNum2ShowValue(oldVal))
}
// 再赋新值,这样就会出现变化的效果
setTimeout(() => {
self.setNumberTransform(this.orderNum)
}, this.time * 1000)
})
},
// 设置文字滚动
setNumberTransform(numStr) {
const numberItems = this.$refs.numberItem
const numberArr = numStr.filter(item => !isNaN(item))
for (let index = 1; index <= numberArr.length; index++) {
// 倒序赋值,保证出现位数变化的时候,0出现在第一个,比如从9999变为10001,会先变成09999,在变成10001
const elem = numberItems[numberItems.length - index]
elem.style.transform = `translate(-50%, -${numberArr[numberArr.length - index] * 10}%)`
}
},
getRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
},
// 处理传过来的具体值value
transNum2ShowValue(num) {
if (this.fillZero) {
if (num.length < this.numberLength) {
num = this.preZeroFill(num, this.numberLength)
} else {
num = this.numberWithDot(num)
}
} else {
num = this.numberWithDot(num)
}
return num.split('')
},
numberWithDot(number) {
const str = []
if (typeof number === 'number') {
number = String(number)
}
if (number.length <= 3) {
return number
}
String(number)
.split('')
.reverse()
.forEach((item, index) => {
if (index !== 0 && index % 3 === 0) {
str.push(',', item)
} else {
str.push(item)
}
})
return str.reverse().join('')
},
preZeroFill(num, size) {
if (num >= Math.pow(10, size)) {
// 如果num本身位数不小于size位
return num.toString()
} else {
var _str = Array(size + 1).join('0') + num
return _str.slice(_str.length - size)
}
}
}
}
</script>
<style scoped lang='scss'>
/*具体值value总量滚动数字设置*/
.box-item {
position: relative;
height: 50px;
font-size: 27px;
line-height: 20px;
text-align: center;
list-style: none;
color: #2d7cff;
writing-mode: vertical-lr;
text-orientation: upright;
/*文字禁止编辑*/
-moz-user-select: none; /*火狐*/
-webkit-user-select: none; /*webkit浏览器*/
-ms-user-select: none; /*IE10*/
-khtml-user-select: none; /*早期浏览器*/
user-select: none;
/* overflow: hidden; */
}
/* 默认逗号设置 */
.mark-item {
width: 10px;
height: 50px;
margin-right: 5px;
line-height: 10px;
font-size: 24px;
position: relative;
& > span {
position: absolute;
width: 100%;
bottom: 0;
writing-mode: vertical-rl;
text-orientation: upright;
}
}
/*滚动数字设置*/
.number-item {
width: 41px;
height: 50px;
background: #ccc;
list-style: none;
margin-right: 5px;
background: rgba(250, 250, 250, 1);
border-radius: 4px;
border: 1px solid rgba(221, 221, 221, 1);
& > span {
position: relative;
display: inline-block;
margin-right: 10px;
width: 100%;
height: 100%;
writing-mode: vertical-rl;
text-orientation: upright;
overflow: hidden;
& > i {
font-style: normal;
position: absolute;
top: 11px;
left: 50%;
transform: translate(-50%, 0);
transition: transform 1s ease-in-out;
letter-spacing: 10px;
}
}
}
.number-item:last-child {
margin-right: 0;
}
</style>
外部引用组件:
<template>
<el-container>
<FlipNumber :number="num1" :fill-zero="true" />
</el-container>
</template>
<script>
import FlipNumber from '@/views/components/common/FlipNumber.vue'
export default {
components: {
FlipNumber
},
data() {
return {
num1: 3200
}
},
mounted() {
setInterval(() => {
this.num1 = this.num1 + 1521
}, 2000)
},
methods: {
}
}
</script>
附上链接: