这是一个vue3 + scss的数字滚动效果
先容:当数字变化时,只改变变化的数字位,其余的不变,可以递增、递减、骤变、负数也可以,但是样式要根据详细的项目需求去改;
效果1、增加数字:
https://i-blog.csdnimg.cn/direct/014eaf09d798406d9c6d8f9abc688337.gif
效果2、淘汰数字:
https://i-blog.csdnimg.cn/direct/bf80a72762fb4237b7834bf34de8ef03.gif
使用方法:
<template>
<AnimatNumber :data="data" />
</template>
<script setup>
// 引入动画
import AnimatNumber from "./components/AnimatNumber.vue";
const data = ref(0);
setInterval(() => {
data.value -= 30;
}, 2000);
</script>
<style lang="scss">
</style>
组件代码(vue3):
<template>
<div class="num-wrap">
<div v-for="(item, index) in computedData" :key="index" class="num-item">
<div class="num-inner" ref="numInnerRef">
<div class="prev">{{ item.newValue }}</div>
<div class="current">{{ item.oldValue }}</div>
<div class="next">{{ item.oldValue }}</div>
</div>
</div>
</div>
</template>
<script setup>
// 数字滚动效果
import { onBeforeUnmount, watch, ref, nextTick } from "vue";
const props = defineProps({
// 传进来的数据number、string的number都可以
data: {
type: ,
default: 999
},
// 动画持续时间number、string的number都可以最低1000ms
duration: {
type: ,
default: 500
},
// 基本的高度 所有的动画移动距离都是和这个有关的,确保这个值和css的$height一样,否则有问题
baseHeight: {
type: Number,
default: 50
}
});
const numInnerRef = ref();
// raf演示器
const setTimeoutPolyfill = (func, delay) => {
let startTime = Date.now();
let rafId;
function animationFrameCallback() {
const currentTime = Date.now();
const timeElapsed = currentTime - startTime;
if (timeElapsed >= delay) {
func();
} else {
rafId = requestAnimationFrame(animationFrameCallback);
}
}
rafId = requestAnimationFrame(animationFrameCallback);
// 返回一个取消函数
return () => cancelAnimationFrame(rafId);
};
/*
推演公式
新 旧
1001-> 1000
1002-> 1001
1003-> 1002
1004-> 1003
1005-> 1004
*/
const newArr = ref([]);
const oldArr = ref([]);
const computedData = ref(
props.data
.toString()
.split("")
.map((item, index) => ({ index, oldValue: item, newValue: item }))
);
const lock = ref(false);
// 延时器
const timer = ref({
timerOne: null,
timerTwo: null
});
watch(
() => props.data,
(newVal, oldVal) => {
if (`${newVal}`.length !== `${oldVal}`.length) {
lock.value = false;
}
if (!lock.value) {
computedData.value = props.data
.toString()
.split("")
.map((item, index) => ({ index, oldValue: item, newValue: item }));
lock.value = true;
}
newArr.value = newVal
.toString()
.split("")
.map((item, index) => ({ index, value: item }));
oldArr.value = oldVal
.toString()
.split("")
.map((item, index) => ({ index, value: item }));
/*
如果newArr的长度大于于oldArr的长度,则需要给oldArr从前面增加newArr.length - oldArr.length的长度的{ index, oldValue: '-', newValue: newValueItem },
同时更新oldArr没有新增的index
*/
// 新值和老值差
const differLength = newArr.value.length - oldArr.value.length;
if (newArr.value.length > oldArr.value.length) {
for (let i = 0; i < differLength; i++) {
oldArr.value.unshift({ index: i, value: "-" });
}
// 重新设置index
oldArr.value.forEach((item, index) => (item.index = index));
}
// 改变的数字的索引集合
const indexArr = [];
newArr.value.forEach(item => {
if (item.value !== oldArr.value.value) {
indexArr.push(item.index);
}
});
nextTick(() => {
indexArr.forEach(diffIndex => {
numInnerRef.value.children.innerHTML =
newArr.value.value;
numInnerRef.value.children.animate(
[{ top: `${-props.baseHeight}px` }, { top: 0 }],
{
duration: props.duration,
fill: "forwards"
}
);
numInnerRef.value.children.animate(
[{ top: "0" }, { top: `${props.baseHeight}px` }],
{
duration: props.duration,
fill: "forwards"
}
);
timer.value.timerOne = setTimeoutPolyfill(() => {
numInnerRef.value.children.innerHTML =
oldArr.value.value;
timer.value.timerTwo = setTimeoutPolyfill(() => {
numInnerRef.value.children.innerHTML =
newArr.value.value;
}, props.duration);
numInnerRef.value[
diffIndex
].children.style.top = `${-props.baseHeight}px`;
}, props.duration);
});
});
},
{ deep: true }
);
// 卸载
onBeforeUnmount(() => {
timer.value.timerOne && timer.value.timerOne();
timer.value.timerTwo && timer.value.timerTwo();
});
</script>
<style lang="scss" scoped>
$width: 50px;
$height: 50px;
.num-wrap {
margin-top: 200px;
display: flex;
gap: 10px;
.num-item {
width: $width;
height: $height;
border: 1px solid #000;
border-radius: 8px;
font-size: 20px;
font-weight: 600;
position: relative;
overflow: hidden;
color: #0dfbff;
background: rgba(0, 13, 23, 0.5);
.num-inner {
position: relative;
width: $width;
height: $height;
}
.prev,
.current,
.next {
width: $width;
height: $height;
text-align: center;
line-height: $width;
position: absolute;
}
.prev {
top: -$height;
}
.current {
top: 0;
}
.next {
top: $height;
}
}
}
</style>
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]