当前位置: 首页 > news >正文

JS实现数字变化时,上下翻滚动画效果

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>高级数字翻滚动画</title><script src="https://cdn.tailwindcss.com"></script><link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"><script>tailwind.config = {theme: {extend: {colors: {primary: '#3B82F6',secondary: '#10B981',accent: '#8B5CF6',dark: '#1F2937',number: '#1E293B', // 深色数字颜色},fontFamily: {inter: ['Inter', 'system-ui', 'sans-serif'],},},}}</script><style type="text/tailwindcss">@layer utilities {.content-auto {content-visibility: auto;}.digit-container {position: relative;display: inline-block;height: 1.2em;overflow: hidden;margin: 0 0.05em;background: rgba(255, 255, 255, 0.1);border-radius: 0.15em;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);}.digit-wrapper {display: flex;flex-direction: column;transition: transform 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);}.digit {height: 1.2em;line-height: 1.2em;text-align: center;font-variant-numeric: tabular-nums;padding: 0 0.1em;color: theme('colors.number'); /* 使用深色数字颜色 */}.separator {margin: 0 0.15em;color: theme('colors.number/70'); /* 分隔符使用稍浅的颜色 */}.btn-pulse {animation: pulse 2s infinite;}@keyframes pulse {0% {box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);}70% {box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);}100% {box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);}}}</style>
</head>
<body class="bg-gradient-to-br from-gray-50 to-gray-100 min-h-screen font-inter text-dark"><div class="container mx-auto px-4 py-16 max-w-5xl"><header class="text-center mb-12"><h1 class="text-[clamp(2rem,5vw,3.5rem)] font-bold text-dark mb-4">高级数字翻滚动画</h1><p class="text-gray-600 text-lg max-w-2xl mx-auto">体验流畅的数字变化动画效果,当设置新数字时,当前数字会平滑滚动到目标值</p></header><main class="bg-white rounded-3xl shadow-xl p-8 mb-12 transform hover:shadow-2xl transition-all duration-300 relative overflow-hidden"><!-- 装饰元素 --><div class="absolute -top-10 -right-10 w-40 h-40 bg-primary/10 rounded-full blur-3xl"></div><div class="absolute -bottom-16 -left-16 w-48 h-48 bg-accent/10 rounded-full blur-3xl"></div><div class="flex flex-col md:flex-row gap-8 items-center justify-center relative z-10"><div class="number-display mb-8 md:mb-0 w-full max-w-md"><div id="number-display" class="text-[clamp(2.5rem,8vw,5rem)] font-bold flex justify-center flex-wrap"><!-- 数字将在这里动态生成 --></div></div><div class="controls w-full md:w-auto"><div class="bg-white/80 backdrop-blur-sm p-6 rounded-2xl shadow-lg border border-gray-100"><h2 class="text-xl font-semibold mb-4 text-center">设置数字</h2><div class="mb-5"><label for="target-number" class="block text-sm font-medium text-gray-700 mb-1.5">目标数字</label><div class="relative"><span class="absolute inset-y-0 left-0 flex items-center pl-3 text-gray-500"><i class="fa fa-hashtag"></i></span><input type="number" id="target-number" class="w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all" placeholder="输入目标数字" value="12345"></div></div><div class="mb-5"><label for="animation-duration" class="block text-sm font-medium text-gray-700 mb-1.5">动画速度</label><div class="relative"><input type="range" id="animation-duration" min="300" max="3000" step="100" value="1500" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary"><div class="flex justify-between text-xs text-gray-500 mt-1"><span></span><span id="duration-value">中等</span><span></span></div></div></div><div class="mb-5"><label class="block text-sm font-medium text-gray-700 mb-1.5">数字格式</label><div class="grid grid-cols-2 gap-3"><label class="flex items-center p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-all"><input type="radio" name="number-format" value="comma" checked class="form-radio text-primary focus:ring-primary"><span class="ml-2 text-sm text-gray-700">逗号分隔 (12,345)</span></label><label class="flex items-center p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-all"><input type="radio" name="number-format" value="none" class="form-radio text-primary focus:ring-primary"><span class="ml-2 text-sm text-gray-700">无分隔 (12345)</span></label></div></div><div class="mb-5"><label class="block text-sm font-medium text-gray-700 mb-1.5">动画类型</label><div class="grid grid-cols-2 gap-3"><label class="flex items-center p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-all"><input type="radio" name="animation-type" value="normal" checked class="form-radio text-primary focus:ring-primary"><span class="ml-2 text-sm text-gray-700">普通滚动</span></label><label class="flex items-center p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 transition-all"><input type="radio" name="animation-type" value="random" class="form-radio text-primary focus:ring-primary"><span class="ml-2 text-sm text-gray-700">随机滚动</span></label></div></div><button id="set-number" class="w-full bg-gradient-to-r from-primary to-accent hover:opacity-95 text-white font-medium py-3 px-4 rounded-lg transition-all duration-300 transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-primary/50 btn-pulse"><i class="fa fa-refresh mr-2"></i>应用动画</button></div></div></div></main><section class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12"><div class="bg-white rounded-xl shadow-md p-6 hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1"><div class="w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center mb-4"><i class="fa fa-tachometer text-xl text-primary"></i></div><h3 class="text-lg font-semibold mb-2">实时统计</h3><p class="text-gray-600">适合展示实时变化的数据,如网站访问量、销售额或股票价格等,让数字变化更加生动直观。</p></div><div class="bg-white rounded-xl shadow-md p-6 hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1"><div class="w-12 h-12 bg-secondary/10 rounded-full flex items-center justify-center mb-4"><i class="fa fa-line-chart text-xl text-secondary"></i></div><h3 class="text-lg font-semibold mb-2">数据可视化</h3><p class="text-gray-600">为数据图表添加动画效果,使数据变化过程更加清晰,增强数据展示的视觉冲击力。</p></div><div class="bg-white rounded-xl shadow-md p-6 hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1"><div class="w-12 h-12 bg-accent/10 rounded-full flex items-center justify-center mb-4"><i class="fa fa-gamepad text-xl text-accent"></i></div><h3 class="text-lg font-semibold mb-2">游戏计分</h3><p class="text-gray-600">用于游戏中的计分系统,当分数变化时提供流畅的动画效果,提升游戏体验。</p></div></section><footer class="text-center text-gray-500 text-sm"><p>高级数字翻滚动画 &copy; 2025</p></footer></div><script>// 初始化数字显示let currentNumber = 0;const numberDisplay = document.getElementById('number-display');const targetNumberInput = document.getElementById('target-number');const animationDurationInput = document.getElementById('animation-duration');const durationValue = document.getElementById('duration-value');const setNumberButton = document.getElementById('set-number');const numberFormatRadios = document.querySelectorAll('input[name="number-format"]');const animationTypeRadios = document.querySelectorAll('input[name="animation-type"]');// 更新显示的数字function updateNumberDisplay(number) {// 清除现有内容numberDisplay.innerHTML = '';// 获取当前的数字格式设置const formatSetting = Array.from(numberFormatRadios).find(radio => radio.checked).value;// 根据格式设置处理数字let formattedNumber;if (formatSetting === 'comma') {formattedNumber = number.toLocaleString('en-US');} else {formattedNumber = number.toString();}// 处理可能的逗号分隔符const parts = formattedNumber.split(',');// 为每个数字位创建一个容器和数字包装器let digitCount = 0;parts.forEach((part, partIndex) => {for (let i = 0; i < part.length; i++) {const digit = part[i];const digitContainer = document.createElement('div');digitContainer.className = 'digit-container';const digitWrapper = document.createElement('div');digitWrapper.className = 'digit-wrapper';digitWrapper.dataset.current = digit;// 为每个可能的数字创建一个元素for (let j = 0; j < 10; j++) {const digitElement = document.createElement('div');digitElement.className = 'digit';digitElement.textContent = j;digitWrapper.appendChild(digitElement);}// 计算初始位置 - 修复版本const translateY = -parseInt(digit) * 10; // 每个数字占100%高度digitWrapper.style.transform = `translateY(${translateY}%)`;digitContainer.appendChild(digitWrapper);numberDisplay.appendChild(digitContainer);digitCount++;// 添加分隔符(千位分隔符)if (formatSetting === 'comma' && i === part.length - 1 && partIndex < parts.length - 1) {const separator = document.createElement('span');separator.className = 'separator';separator.textContent = ',';numberDisplay.appendChild(separator);}}});}// 随机滚动动画function animateWithRandomRoll(targetNumber, duration = 1500) {if (isNaN(targetNumber)) return;const digits = numberDisplay.querySelectorAll('.digit-wrapper');const currentNumberString = currentNumber.toString();const targetNumberString = targetNumber.toString();// 确保两个数字字符串长度相同,不足的补零const maxLength = Math.max(currentNumberString.length, targetNumberString.length);const paddedCurrent = currentNumberString.padStart(maxLength, '0');const paddedTarget = targetNumberString.padStart(maxLength, '0');// 如果数字位数不同,重新初始化显示if (digits.length !== maxLength) {updateNumberDisplay(targetNumber);currentNumber = targetNumber;return;}// 为每个数字位设置动画for (let i = 0; i < maxLength; i++) {const currentDigit = parseInt(paddedCurrent[i]);const targetDigit = parseInt(paddedTarget[i]);if (currentDigit !== targetDigit) {const digitWrapper = digits[i];// 计算随机滚动的帧数const randomRolls = 3 + Math.floor(Math.random() * 5); // 3-7次随机滚动const totalFrames = 60 * (duration / 1000); // 假设60fpsconst framesPerRoll = Math.max(10, Math.floor(totalFrames / randomRolls));let currentFrame = 0;let currentDisplayedDigit = currentDigit;// 创建一个动画循环const rollInterval = setInterval(() => {currentFrame++;// 随机滚动if (currentFrame % framesPerRoll === 0 && currentFrame < totalFrames - framesPerRoll) {currentDisplayedDigit = Math.floor(Math.random() * 10);digitWrapper.style.transform = `translateY(${-currentDisplayedDigit * 10}%)`;}// 接近尾声时,滚动到目标数字if (currentFrame >= totalFrames - framesPerRoll) {clearInterval(rollInterval);digitWrapper.style.transitionDuration = `${framesPerRoll * 16.67}ms`; // 16.67ms ≈ 1/60秒digitWrapper.style.transform = `translateY(${-targetDigit * 10}%)`;digitWrapper.dataset.current = targetDigit;}}, 16.67); // 约60fps}}currentNumber = targetNumber;}// 普通滚动动画function animateWithNormalRoll(targetNumber, duration = 1500) {if (isNaN(targetNumber)) return;const digits = numberDisplay.querySelectorAll('.digit-wrapper');const currentNumberString = currentNumber.toString();const targetNumberString = targetNumber.toString();// 确保两个数字字符串长度相同,不足的补零const maxLength = Math.max(currentNumberString.length, targetNumberString.length);const paddedCurrent = currentNumberString.padStart(maxLength, '0');const paddedTarget = targetNumberString.padStart(maxLength, '0');// 如果数字位数不同,重新初始化显示if (digits.length !== maxLength) {updateNumberDisplay(targetNumber);currentNumber = targetNumber;return;}// 为每个数字位设置动画for (let i = 0; i < maxLength; i++) {const currentDigit = parseInt(paddedCurrent[i]);const targetDigit = parseInt(paddedTarget[i]);if (currentDigit !== targetDigit) {const digitWrapper = digits[i];digitWrapper.style.transitionDuration = `${duration}ms`;// 计算目标位置 - 修复版本const translateY = -targetDigit * 10; // 每个数字占100%高度digitWrapper.style.transform = `translateY(${translateY}%)`;digitWrapper.dataset.current = targetDigit;}}currentNumber = targetNumber;}// 滚动到目标数字function animateToNumber(targetNumber, duration = 1500) {// 获取当前的动画类型设置const animationType = Array.from(animationTypeRadios).find(radio => radio.checked).value;if (animationType === 'random') {animateWithRandomRoll(targetNumber, duration);} else {animateWithNormalRoll(targetNumber, duration);}}// 更新速度显示文本function updateSpeedDisplay() {const duration = parseInt(animationDurationInput.value);let speedText;if (duration < 800) {speedText = "极快";} else if (duration < 1200) {speedText = "快速";} else if (duration < 1800) {speedText = "中等";} else if (duration < 2500) {speedText = "慢速";} else {speedText = "极慢";}durationValue.textContent = speedText;}// 初始化显示updateNumberDisplay(currentNumber);updateSpeedDisplay();// 更新动画时长显示animationDurationInput.addEventListener('input', updateSpeedDisplay);// 设置数字按钮点击事件setNumberButton.addEventListener('click', () => {const targetNumber = parseInt(targetNumberInput.value);const duration = parseInt(animationDurationInput.value);animateToNumber(targetNumber, duration);// 添加按钮动画效果setNumberButton.classList.add('scale-95');setTimeout(() => {setNumberButton.classList.remove('scale-95');}, 150);});// 添加键盘支持targetNumberInput.addEventListener('keydown', (e) => {if (e.key === 'Enter') {setNumberButton.click();}});// 格式设置变更事件numberFormatRadios.forEach(radio => {radio.addEventListener('change', () => {updateNumberDisplay(currentNumber);});});// 动画类型变更事件animationTypeRadios.forEach(radio => {radio.addEventListener('change', () => {// 可以在这里添加动画类型变更的视觉反馈});});// 初始随机动画演示setTimeout(() => {animateToNumber(56789, 2000);}, 500);</script>
</body>
</html>
http://www.dtcms.com/a/299966.html

相关文章:

  • 本地部署智能家居集成解决方案 ESPHome 并实现外部访问
  • 五分钟系列-文本搜索工具grep
  • 【工具】好用的浏览器AI助手
  • 【MySQL】VARCHAR(10) 和 VARCHAR(100) 的区别
  • 大模型蒸馏(distillation)---从DeepseekR1-1.5B到Qwen-2.5-1.5B蒸馏
  • 拒绝SQL恐惧:用Python+pyqt打造任意Excel数据库查询系统
  • C++ - 仿 RabbitMQ 实现消息队列--服务端核心模块实现(四)
  • 丝杆升降机应用在食品机械行业有什么特殊的要求吗
  • Java BeanUtils 类详解:作用、语法与示例
  • springboot 基于签名的安全通信
  • 深入解析YARN中的FairScheduler与CapacityScheduler:资源分配策略的核心区别
  • Aerospike Java客户端进阶:对象映射与Spring Data集成实战
  • spring Could 高频面试题
  • 【科普】java和html和lvgl生成页面有什么区别,还有什么方法可以生成?
  • 数据库HB OB mysql ck startrocks, ES存储特点,以及应用场景
  • 通过服务启动应用的流程(类似SystemUi启动流程)
  • Linux笔记5——常用命令-4
  • 深入浅出学习 KNN 算法:从原理到数字识别实践
  • 【Linux庖丁解牛】— 日志进程池 !
  • 大模型系列——Dify:知识库与外部知识库
  • SSH连接失败排查与解决教程: Connection refused
  • PromQL完全指南:掌握Prometheus核心查询语言
  • Ubuntu 22.04 配置 Zsh + Oh My Zsh + Powerlevel10k
  • 二十八、【Linux系统域名解析】DNS安装、子域授权、缓存DNS、分离解析、多域名解析
  • C++___快速入门(上)
  • 人工智能之数学基础:概率论之韦恩图的应用
  • WebAPIs里的filter
  • Android 编码规范全指南
  • 驱动-设备树-基本语法
  • Python爬虫实战:诗词名句网《三国演义》全集