这是一个vue3 + scss的数字滚动效果

打印 上一主题 下一主题

主题 822|帖子 822|积分 2466

先容:
        当数字变化时,只改变变化的数字位,其余的不变,可以递增、递减、骤变、负数也可以,但是样式要根据详细的项目需求去改;
效果1、增加数字:

效果2、淘汰数字:

使用方法:
  1. <template>
  2.   <AnimatNumber :data="data" />
  3. </template>
  4.   
  5. <script setup>
  6. // 引入动画
  7. import AnimatNumber from "./components/AnimatNumber.vue";
  8. const data = ref(0);
  9. setInterval(() => {
  10.   data.value -= 30;
  11. }, 2000);
  12. </script>
  13.   
  14. <style lang="scss">
  15. </style>
  16.   
复制代码
组件代码(vue3):
  1. <template>
  2.   <div class="num-wrap">
  3.     <div v-for="(item, index) in computedData" :key="index" class="num-item">
  4.       <div class="num-inner" ref="numInnerRef">
  5.         <div class="prev">{{ item.newValue }}</div>
  6.         <div class="current">{{ item.oldValue }}</div>
  7.         <div class="next">{{ item.oldValue }}</div>
  8.       </div>
  9.     </div>
  10.   </div>
  11. </template>
  12. <script setup>
  13. // 数字滚动效果
  14. import { onBeforeUnmount, watch, ref, nextTick } from "vue";
  15. const props = defineProps({
  16.   // 传进来的数据  number、string的number都可以
  17.   data: {
  18.     type: [Number, String],
  19.     default: 999
  20.   },
  21.   // 动画持续时间  number、string的number都可以  最低1000ms
  22.   duration: {
  23.     type: [Number, String],
  24.     default: 500
  25.   },
  26.   // 基本的高度 所有的动画移动距离都是和这个有关的,确保这个值和css的$height一样,否则有问题
  27.   baseHeight: {
  28.     type: Number,
  29.     default: 50
  30.   }
  31. });
  32. const numInnerRef = ref();
  33. // raf演示器
  34. const setTimeoutPolyfill = (func, delay) => {
  35.   let startTime = Date.now();
  36.   let rafId;
  37.   function animationFrameCallback() {
  38.     const currentTime = Date.now();
  39.     const timeElapsed = currentTime - startTime;
  40.     if (timeElapsed >= delay) {
  41.       func();
  42.     } else {
  43.       rafId = requestAnimationFrame(animationFrameCallback);
  44.     }
  45.   }
  46.   rafId = requestAnimationFrame(animationFrameCallback);
  47.   // 返回一个取消函数
  48.   return () => cancelAnimationFrame(rafId);
  49. };
  50. /*
  51. 推演公式
  52.     新          旧
  53.     1001  ->   1000
  54.     1002  ->   1001
  55.     1003  ->   1002
  56.     1004  ->   1003
  57.     1005  ->   1004
  58. */
  59. const newArr = ref([]);
  60. const oldArr = ref([]);
  61. const computedData = ref(
  62.   props.data
  63.     .toString()
  64.     .split("")
  65.     .map((item, index) => ({ index, oldValue: item, newValue: item }))
  66. );
  67. const lock = ref(false);
  68. // 延时器
  69. const timer = ref({
  70.   timerOne: null,
  71.   timerTwo: null
  72. });
  73. watch(
  74.   () => props.data,
  75.   (newVal, oldVal) => {
  76.     if (`${newVal}`.length !== `${oldVal}`.length) {
  77.       lock.value = false;
  78.     }
  79.     if (!lock.value) {
  80.       computedData.value = props.data
  81.         .toString()
  82.         .split("")
  83.         .map((item, index) => ({ index, oldValue: item, newValue: item }));
  84.       lock.value = true;
  85.     }
  86.     newArr.value = newVal
  87.       .toString()
  88.       .split("")
  89.       .map((item, index) => ({ index, value: item }));
  90.     oldArr.value = oldVal
  91.       .toString()
  92.       .split("")
  93.       .map((item, index) => ({ index, value: item }));
  94.     /*
  95.       如果newArr的长度大于于oldArr的长度,则需要给oldArr从前面增加newArr.length - oldArr.length的长度的{ index, oldValue: '-', newValue: newValueItem },
  96.       同时更新oldArr没有新增的index
  97.     */
  98.     // 新值和老值差
  99.     const differLength = newArr.value.length - oldArr.value.length;
  100.     if (newArr.value.length > oldArr.value.length) {
  101.       for (let i = 0; i < differLength; i++) {
  102.         oldArr.value.unshift({ index: i, value: "-" });
  103.       }
  104.       // 重新设置index
  105.       oldArr.value.forEach((item, index) => (item.index = index));
  106.     }
  107.     // 改变的数字的索引集合
  108.     const indexArr = [];
  109.     newArr.value.forEach(item => {
  110.       if (item.value !== oldArr.value[item.index].value) {
  111.         indexArr.push(item.index);
  112.       }
  113.     });
  114.     nextTick(() => {
  115.       indexArr.forEach(diffIndex => {
  116.         numInnerRef.value[diffIndex].children[0].innerHTML =
  117.           newArr.value[diffIndex].value;
  118.         numInnerRef.value[diffIndex].children[0].animate(
  119.           [{ top: `${-props.baseHeight}px` }, { top: 0 }],
  120.           {
  121.             duration: props.duration,
  122.             fill: "forwards"
  123.           }
  124.         );
  125.         numInnerRef.value[diffIndex].children[1].animate(
  126.           [{ top: "0" }, { top: `${props.baseHeight}px` }],
  127.           {
  128.             duration: props.duration,
  129.             fill: "forwards"
  130.           }
  131.         );
  132.         timer.value.timerOne = setTimeoutPolyfill(() => {
  133.           numInnerRef.value[diffIndex].children[2].innerHTML =
  134.             oldArr.value[diffIndex].value;
  135.           timer.value.timerTwo = setTimeoutPolyfill(() => {
  136.             numInnerRef.value[diffIndex].children[1].innerHTML =
  137.               newArr.value[diffIndex].value;
  138.           }, props.duration);
  139.           numInnerRef.value[
  140.             diffIndex
  141.           ].children[2].style.top = `${-props.baseHeight}px`;
  142.         }, props.duration);
  143.       });
  144.     });
  145.   },
  146.   { deep: true }
  147. );
  148. // 卸载
  149. onBeforeUnmount(() => {
  150.   timer.value.timerOne && timer.value.timerOne();
  151.   timer.value.timerTwo && timer.value.timerTwo();
  152. });
  153. </script>
  154. <style lang="scss" scoped>
  155. $width: 50px;
  156. $height: 50px;
  157. .num-wrap {
  158.   margin-top: 200px;
  159.   display: flex;
  160.   gap: 10px;
  161.   .num-item {
  162.     width: $width;
  163.     height: $height;
  164.     border: 1px solid #000;
  165.     border-radius: 8px;
  166.     font-size: 20px;
  167.     font-weight: 600;
  168.     position: relative;
  169.     overflow: hidden;
  170.     color: #0dfbff;
  171.     background: rgba(0, 13, 23, 0.5);
  172.     .num-inner {
  173.       position: relative;
  174.       width: $width;
  175.       height: $height;
  176.     }
  177.     .prev,
  178.     .current,
  179.     .next {
  180.       width: $width;
  181.       height: $height;
  182.       text-align: center;
  183.       line-height: $width;
  184.       position: absolute;
  185.     }
  186.     .prev {
  187.       top: -$height;
  188.     }
  189.     .current {
  190.       top: 0;
  191.     }
  192.     .next {
  193.       top: $height;
  194.     }
  195.   }
  196. }
  197. </style>
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万有斥力

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表