先容:
当数字变化时,只改变变化的数字位,其余的不变,可以递增、递减、骤变、负数也可以,但是样式要根据详细的项目需求去改;
效果1、增加数字:
效果2、淘汰数字:
使用方法:
- <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: [Number, String],
- default: 999
- },
- // 动画持续时间 number、string的number都可以 最低1000ms
- duration: {
- type: [Number, String],
- 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[item.index].value) {
- indexArr.push(item.index);
- }
- });
- nextTick(() => {
- indexArr.forEach(diffIndex => {
- numInnerRef.value[diffIndex].children[0].innerHTML =
- newArr.value[diffIndex].value;
- numInnerRef.value[diffIndex].children[0].animate(
- [{ top: `${-props.baseHeight}px` }, { top: 0 }],
- {
- duration: props.duration,
- fill: "forwards"
- }
- );
- numInnerRef.value[diffIndex].children[1].animate(
- [{ top: "0" }, { top: `${props.baseHeight}px` }],
- {
- duration: props.duration,
- fill: "forwards"
- }
- );
- timer.value.timerOne = setTimeoutPolyfill(() => {
- numInnerRef.value[diffIndex].children[2].innerHTML =
- oldArr.value[diffIndex].value;
- timer.value.timerTwo = setTimeoutPolyfill(() => {
- numInnerRef.value[diffIndex].children[1].innerHTML =
- newArr.value[diffIndex].value;
- }, props.duration);
- numInnerRef.value[
- diffIndex
- ].children[2].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企服之家,中国第一个企服评测及商务社交产业平台。 |