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

介绍:

        当数字变化时,只改变变化的数字位,其余的不变,可以递增、递减、骤变、负数也可以,但是样式要根据具体的项目需求去改;

效果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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938980.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pytest-Bdd vs Behave:选择最适合的 Python BDD 框架

Pytest-Bdd vs Behave&#xff1a;选择最适合的 Python BDD 框架 Pytest BDD vs Behave&#xff1a;选择最适合的 Python BDD 框架BDD 介绍Python BDD 框架列表Python BehavePytest BDDPytest BDD vs Behave&#xff1a;关键区别Pytest BDD vs Behave&#xff1a;最佳应用场景结…

【Unity3D】无限循环列表(扩展版)

基础版&#xff1a;【Unity技术分享】UGUI之ScrollRect优化_ugui scrollrect 优化-CSDN博客 using UnityEngine; using UnityEngine.UI; using System.Collections.Generic;public delegate void OnBaseLoopListItemCallback(GameObject cell, int index); public class BaseLo…

【Elasticsearch】使用阿里云 infererence API 及 semantic text 进行向量搜索

原作者&#xff1a;Elastic布道师 刘晓国 在之前的文章 “Elasticsearch 开放推理 API 新增阿里云 AI 搜索支持”&#xff0c;它详细描述了如何使用 Elastic inference API 来针对阿里的密集向量模型&#xff0c;稀疏向量模型&#xff0c; 重新排名及 completion 进行展示。在…

景联文科技:精准语音标注,驱动语音技术新发展

在人工智能迅速发展的今天&#xff0c;语音技术的应用已经渗透到我们生活的方方面面。从智能音箱、语音助手到自动语音识别系统&#xff0c;高质量的语音数据是这些应用成功的关键。景联文科技作为领先的AI数据服务提供商&#xff0c;专注于为客户提供高精度、高效的语音标注服…

windows免登录linux

windows 生成秘钥文件 ssh-keygen -t rsa 将公钥传送到服务器 scp C:\Users\xx/.ssh/id_rsa.pub xxxx:/home/ruoyi/id_rsa.pub linux 使用ssh-copy-id -i ~/.ssh/id_rsa.pub userhost 如果禁用root登录&#xff0c;先开启 vim /etc/ssh/sshd_config PermitRootLogin yes …

基于容器的云原生,让业务更自由地翱翔云端

无论是要构建一个应用或开发一个更庞大的解决方案&#xff0c;在技术选型时&#xff0c;技术的开放性和可移植性已经成为很多企业优先考虑的问题之一。毕竟没人希望自己未来的发展方向和成长速度被自己若干年前选择使用的某项技术所限制或拖累。 那么当你的业务已经上云&#x…

Visual Studio 使用 GitHub Copilot 协助调试

&#x1f380;&#x1f380;&#x1f380;【AI辅助编程系列】&#x1f380;&#x1f380;&#x1f380; Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码Visual Studio 安装和管理 GitHub CopilotVisual Studio 使用 GitHub Copilot 扩展Visual Studio 使用 GitHu…

springboot限流注解

我们在工作中 有些接口访问量过大 为了保证服务的正常运行可以增加限流 第一步 引入aop和redis <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency…

MySQL多表查询时有哪些连接方式?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL多表查询时有哪些连接方式?】面试题。希望对大家有帮助&#xff1b; MySQL多表查询时有哪些连接方式? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 MySQL 中进行多表查询时&#xff0c;常见的连接方式有以下…

Python | 虚拟环境01 - 什么是虚拟环境、它的由来

导言 python3真的不是安装了就完事&#xff0c;必须理解虚拟环境是什么才算是初步掌握python环境。 学习python3虚拟环境&#xff0c;建议参考B站教程。这位博主用了6个视频&#xff0c;每一个视频仅仅几分钟。居然把python3的虚拟环境讲明白了。 虚拟环境&#xff08;Virtual…

【已解决】在Visual Studio里将应用与Microsoft Store关联时提示网络异常

发布Windows应用时。在Visual Studio里点击"发布“&#xff0c;将应用与Microsoft Store关联时&#xff0c;一直提示网络错误。 查了一下论坛&#xff0c;发现之前也经常出现&#xff0c;但我是第一次遇到。 不能就这样一直被卡着呀&#xff0c;研究了一下&#xff0c;还…

html基础-认识html

1.什么是html html是浏览器可以识别的的标记语言&#xff0c;我们在浏览器浏览的网页就是一个个的html文档 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>认识html</title> </head> <body><h1…

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(四)

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(四) 你好,我是拉依达。 感谢所有阅读关注我的同学支持,目前博客累计阅读 27w,关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客》已经是 Linux驱动 相关内容搜索的推荐首位,感谢大家支持。 《拉…

MySQL 深入理解隔离性

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 MySQL 深入理解隔离性 收录于专栏[MySQL] 本专栏旨在分享学习MySQL的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 由于之前在 MySQL 事务特…

Nacos 3.0 考虑升级到 Spring Boot 3 + JDK 17 了!

Nacos 由阿里开源&#xff0c;是 Spring Cloud Alibaba 中的一个重要组件&#xff0c;主要用于发现、配置和管理微服务。 由于 Spring Boot 2 的维护已于近期停止&#xff0c;Nacos 团队考虑升级到 Spring Boot 3 JDK 17&#xff0c;目前正在征求意见和建议。 这其实是一件好…

【硬件接口】I2C总线接口

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、概述 I2C总线是一种非常常用的总线&#xff0c;其多用于一个主机&#xff08;或多个&#xff09;与单个或多个从设备通讯…

监控视频汇聚融合云平台一站式解决视频资源管理痛点

随着5G技术的广泛应用&#xff0c;各领域都在通信技术加持下通过海量终端设备收集了大量视频、图像等物联网数据&#xff0c;并通过人工智能、大数据、视频监控等技术方式来让我们的世界更安全、更高效。然而&#xff0c;随着数字化建设和生产经营管理活动的长期开展&#xff0…

GEE+本地XGboot分类

GEE本地XGboot分类 我想做提取耕地提取&#xff0c;想到了一篇董金玮老师的一篇论文&#xff0c;这个论文是先提取的耕地&#xff0c;再做作物分类&#xff0c;耕地的提取代码是开源的。 但这个代码直接在云端上进行分类&#xff0c;GEE会爆内存&#xff0c;因此我准备把数据下…

Spring Boot 集成 MyBatis 全面讲解

Spring Boot 集成 MyBatis 全面讲解 MyBatis 是一款优秀的持久层框架&#xff0c;与 Spring Boot 集成后可以大大简化开发流程。本文将全面讲解如何在 Spring Boot 中集成 MyBatis&#xff0c;包括环境配置、基础操作、高级功能和最佳实践。 一、MyBatis 简介 1. SqlSession …

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:电影院后台管理系统(前后端源码 + 数据库 sql 脚本)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 项目介绍 2.0 用户登录功能 3.0 用户管理功能 4.0 影院管理功能 5.0 电影管理功能 6.0 影厅管理功能 7.0 电影排片管理功能 8.0 用户评论管理功能 9.0 用户购票功…