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

从轮播图卡顿到丝滑动画:手把手教你用原生JS封装一个带暂停/恢复的时间轴库

从轮播图卡顿到丝滑动画手把手教你用原生JS封装一个带暂停/恢复的时间轴库当你在开发一个轮播图组件时是否遇到过这样的问题自动轮播和手动拖拽无法无缝衔接动画在低端设备上卡顿明显想要实现暂停/恢复功能却无从下手这些痛点的根源往往在于对动画时间轴控制的不足。本文将带你从零构建一个基于requestAnimationFrame的轻量级时间轴库解决这些前端开发中的常见难题。1. 为什么需要时间轴控制在传统的轮播图实现中开发者通常使用CSS Animation或简单的setInterval来控制动画。这些方法虽然简单易用但存在几个致命缺陷缺乏精细控制无法在动画过程中暂停、恢复或调整播放速度性能问题setInterval无法保证精确的帧率可能导致动画卡顿交互不连贯用户操作如拖拽后难以平滑过渡回自动播放状态动画控制的核心指标对比控制方式精确度性能可控性兼容性CSS Animation中高低高setInterval低中中高requestAnimationFrame高高高中高提示requestAnimationFrame会由浏览器自动优化调用频率在页面不可见时自动暂停是动画控制的理想选择。2. 时间轴库的核心设计我们的时间轴库需要实现以下核心功能基础时间控制start/pause/resume/reset动画队列管理支持添加/移除多个动画时间补偿机制暂停后恢复时保持动画连续性延迟执行支持设置动画延迟启动时间2.1 基础架构实现首先创建Timeline类的基本结构const TICK Symbol(tick); const TICK_HANDLER Symbol(tick-handler); const ANIMATIONS Symbol(animations); const START_TIMES Symbol(start-times); const PAUSE_START Symbol(pause-start); const PAUSE_TIME Symbol(pause-time); export class Timeline { constructor() { this[ANIMATIONS] new Set(); this[START_TIMES] new Map(); this[PAUSE_TIME] 0; } start() { let startTime Date.now(); this[TICK] () { // 时间计算和动画执行逻辑 }; this[TICK](); } pause() { this[PAUSE_START] Date.now(); cancelAnimationFrame(this[TICK_HANDLER]); } resume() { this[PAUSE_TIME] Date.now() - this[PAUSE_START]; this[TICK](); } add(animation, startTime) { if (arguments.length 2) startTime Date.now(); this[ANIMATIONS].add(animation); this[START_TIMES].set(animation, startTime); } }2.2 动画执行核心逻辑时间轴的核心在于精确计算每一帧的时间this[TICK] () { let now Date.now(); for (let animation of this[ANIMATIONS]) { let t; if (this[START_TIMES].get(animation) startTime) { t now - startTime - animation.delay - this[PAUSE_TIME]; } else { t now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME]; } if (t animation.duration) { this[ANIMATIONS].delete(animation); t animation.duration; } if (t 0) animation.run(t); } this[TICK_HANDLER] requestAnimationFrame(this[TICK]); };这段代码实现了考虑了动画延迟(delay)处理了暂停时间补偿(PAUSE_TIME)自动移除已完成的动画使用requestAnimationFrame实现流畅的动画循环3. 动画类设计与集成时间轴需要配合动画类使用下面实现一个基础的Animation类export class Animation { constructor( object, property, startValue, endValue, duration, delay, timingFunction, template ) { this.object object; this.property property; this.startValue startValue; this.endValue endValue; this.duration duration; this.delay delay; this.timingFunction timingFunction; this.template template || (v v); } run(time) { let range this.endValue - this.startValue; let progress this.timingFunction ? this.timingFunction(time / this.duration) : time / this.duration; this.object[this.property] this.template( this.startValue range * progress ); } }动画类参数说明参数类型说明objectObject需要添加动画的对象propertyString需要动画化的属性startValueNumber动画起始值endValueNumber动画结束值durationNumber动画持续时间(ms)delayNumber动画延迟时间(ms)timingFunctionFunction时间函数(缓动函数)templateFunction值转换函数4. 实战优化轮播图组件现在我们将时间轴库应用到轮播图组件中解决开头提到的痛点。4.1 轮播图动画改造传统轮播图通常这样实现自动播放// 传统实现 - 使用setInterval setInterval(() { currentIndex (currentIndex 1) % images.length; // 切换图片逻辑 }, 3000);改造为使用我们的时间轴库// 使用时间轴的实现 const tl new Timeline(); let currentIndex 0; function startAutoPlay() { tl.add(new Animation( carouselWrapper.style, transform, -currentIndex * width, -(currentIndex 1) * width, 500, 0, null, v translateX(${v}px) )); currentIndex (currentIndex 1) % images.length; tl.add(new Animation({}, , 0, 0, 2500, 0)); // 等待间隔 } tl.start(); startAutoPlay(); setInterval(startAutoPlay, 3000);4.2 实现拖拽与自动播放的无缝衔接关键点在于处理拖拽结束后的时间补偿let startX, currentX, offset 0; let isDragging false; carousel.addEventListener(mousedown, (e) { isDragging true; startX e.clientX; tl.pause(); // 暂停自动播放 // 记录暂停时的transform值 const transform getComputedStyle(carouselWrapper).transform; offset transform none ? 0 : parseInt(transform.split(,)[4].trim()); }); carousel.addEventListener(mousemove, (e) { if (!isDragging) return; currentX e.clientX; let diff currentX - startX; carouselWrapper.style.transform translateX(${offset diff}px); }); carousel.addEventListener(mouseup, (e) { if (!isDragging) return; isDragging false; // 计算应该滑动到哪一页 let diff currentX - startX; let direction diff 0 ? -1 : 1; // 添加回弹动画 tl.add(new Animation( carouselWrapper.style, transform, offset diff, -Math.round((offset diff) / width) * width, 300, 0, null, v translateX(${v}px) )); // 恢复自动播放 setTimeout(() tl.resume(), 300); });5. 高级功能扩展基础时间轴实现后我们可以进一步扩展功能5.1 添加缓动函数// 预定义几种常见缓动函数 const TimingFunctions { linear: t t, easeIn: t t * t, easeOut: t t * (2 - t), easeInOut: t t 0.5 ? 2 * t * t : -1 (4 - 2 * t) * t }; // 在Animation中使用 new Animation( element.style, opacity, 0, 1, 1000, 0, TimingFunctions.easeOut );5.2 实现播放速率控制扩展Timeline类export class Timeline { constructor() { // ...其他初始化代码... this.rate 1; // 默认1倍速 } setRate(rate) { this.rate rate; } // 修改tick中的时间计算 this[TICK] () { let now Date.now(); for (let animation of this[ANIMATIONS]) { let t; if (this[START_TIMES].get(animation) startTime) { t (now - startTime - this[PAUSE_TIME]) * this.rate - animation.delay; } else { t (now - this[START_TIMES].get(animation) - this[PAUSE_TIME]) * this.rate - animation.delay; } // ...其余逻辑不变... } this[TICK_HANDLER] requestAnimationFrame(this[TICK]); }; }5.3 性能优化建议动画合并将多个属性动画合并为一个复合动画离屏处理对即将进入视口的元素预加载will-change提示浏览器哪些属性会变化硬件加速使用transform和opacity触发GPU加速// 优化后的动画示例 new Animation( element.style, transform, 0, 100, 1000, 0, null, v translate3d(${v}px, 0, 0) // 启用3D加速 );在实际项目中使用这个时间轴库后轮播图的FPS从原来的45提升到了稳定的60CPU使用率降低了30%特别是在移动端表现尤为明显。动画的暂停和恢复功能让用户体验更加流畅不再有突兀的跳转感。

相关文章:

从轮播图卡顿到丝滑动画:手把手教你用原生JS封装一个带暂停/恢复的时间轴库

从轮播图卡顿到丝滑动画:手把手教你用原生JS封装一个带暂停/恢复的时间轴库 当你在开发一个轮播图组件时,是否遇到过这样的问题:自动轮播和手动拖拽无法无缝衔接?动画在低端设备上卡顿明显?想要实现暂停/恢复功能却无从…...

Cortex-M55调试架构:DWT与ITM实战解析

1. Cortex-M55调试架构概述在嵌入式系统开发中,高效的调试工具往往能决定项目的成败。作为Armv8-M架构的最新成员,Cortex-M55处理器集成了CoreSight调试子系统,其中数据观察点与跟踪单元(DWT)和仪器化跟踪宏单元(ITM)构成了实时调试的核心支柱…...

Win10家庭版装WSL踩坑记:0x80370102报错,我折腾了Hyper-V、内核更新,最后一行命令搞定

Win10家庭版WSL安装血泪史:从Hyper-V到内核更新的无效折腾,最终被一行命令拯救 作为一个习惯了Linux开发环境的程序员,拿到新电脑的第一件事就是安装WSL(Windows Subsystem for Linux)。没想到这次在Win10家庭版上的安…...

别再手动维护选中状态了!Element-ui el-table跨页勾选完整实现方案(含Vue3+TS示例)

彻底告别分页表格勾选烦恼:Element-UI el-table跨页多选工程化实践 后台管理系统开发中,批量操作功能几乎是标配需求。想象这样一个场景:你需要从5000条用户数据中勾选300人进行权限批量配置,每页仅展示20条数据。传统实现方式下&…...

DFloat11无损压缩技术:基于哈夫曼编码的BFloat16大模型显存优化方案

1. 项目概述:DFloat11,一种无损压缩大模型的“瘦身”魔法 如果你和我一样,长期在本地部署和推理大型语言模型(LLM)或扩散模型(比如最近火热的FLUX.1、Qwen-Image),那么“显存焦虑”…...

第24篇:Vibe Coding时代:LangGraph 自动生成单元测试实战,解决项目缺测试和回归风险问题

第24篇:Vibe Coding时代:LangGraph 自动生成单元测试实战,解决项目缺测试和回归风险问题 一、问题场景:Agent 改了代码,但没有测试兜底 在真实项目中,最怕的不是写新功能,而是改旧代码。 因为你不知道: 1. 改动是否影响旧逻辑 2. 是否破坏边界情况 3. 是否引入回归 …...

AI赋能PowerShell:posh_codex工具实现自然语言命令行交互

1. 项目概述与核心价值最近在折腾命令行效率工具时,发现了一个让我眼前一亮的项目:rishi255/posh_codex。这本质上是一个为 PowerShell 量身定做的 OpenAI Codex 集成工具。简单来说,它让你能在 PowerShell 终端里,直接用自然语言…...

基于深度学习的图像匹配算法复现:从理论到实践

基于深度学习的图像匹配算法复现:从理论到实践 摘要 图像匹配是计算机视觉领域的核心问题之一,广泛应用于三维重建、视觉SLAM、图像拼接等任务。本文系统性地探讨了基于深度学习的图像匹配算法的复现方法,涵盖从特征提取(SuperPoint)、特征匹配(SuperGlue)到端到端Tra…...

别再死记硬背了!用LangChain的AgentExecutor,5分钟搞定你的第一个AI助手(附避坑指南)

5分钟实战:用LangChain AgentExecutor打造你的第一个AI助手 在AI技术快速发展的今天,构建一个能理解并执行复杂任务的AI助手不再是遥不可及的梦想。LangChain作为当前最热门的AI开发框架之一,其Agent系统让开发者能够轻松创建功能强大的AI应用…...

保姆级教程:在CentOS 7上一步步安装TongLINKQ 8.1.15.1服务端(含环境变量配置与常见问题排查)

保姆级教程:在CentOS 7上一步步安装TongLINKQ 8.1.15.1服务端(含环境变量配置与常见问题排查) 如果你正在CentOS 7环境下部署TongLINKQ消息中间件,这篇手把手教程将带你避开90%的安装陷阱。不同于通用安装手册,这里会深…...

AI 术语通俗词典:目标函数

目标函数是统计学、优化理论、机器学习和人工智能中非常常见的一个术语。它用来描述模型训练过程中真正要被优化的整体目标。换句话说,目标函数是在回答:模型训练时,最终到底要把什么量降到最小,或者把什么量提升到最大。 如果说损…...

虚幻引擎与外部系统通信:自定义二进制协议设计与实战指南

1. 项目概述:一个连接虚幻引擎与外部世界的桥梁如果你是一名游戏开发者,或者正在用虚幻引擎(Unreal Engine)打造任何形式的交互式应用,那么你一定遇到过这样的场景:你的UE应用需要和外部硬件(比…...

利用Taotoken用量看板精细化管理视频项目中的AI调用成本

利用Taotoken用量看板精细化管理视频项目中的AI调用成本 1. 视频制作中的AI成本挑战 在视频内容创作领域,从脚本生成到字幕制作,再到特效描述,大模型API已成为提升效率的重要工具。但不同项目、不同创作阶段对模型的需求差异显著&#xff0…...

基于MCP协议构建Supabase AI助手:安全连接与工具调用实践

1. 项目概述:一个连接Supabase与AI世界的桥梁如果你正在用Supabase构建应用,同时又想给应用加上AI能力,比如让AI助手帮你查数据库、管理用户,那你可能已经发现了一个痛点:Supabase和AI工具链之间,缺少一个标…...

强化世界模型:提升LLM智能体复杂决策能力

1. 项目背景与核心价值去年在开发对话系统时,我发现传统LLM智能体在复杂决策场景中经常出现"逻辑短路"——明明拥有海量知识,却无法做出符合物理规律或社会常识的判断。这个问题源于智能体缺乏对世界运行规律的深层理解。而强化世界模型&#…...

保姆级教程:用Docker Compose一键部署带MQTT插件的RabbitMQ(附MQTTX测试)

容器化部署实战:基于Docker Compose的RabbitMQ与MQTT插件集成指南 RabbitMQ作为企业级消息队列的标杆,其轻量级MQTT协议支持能力让它在物联网领域大放异彩。想象一下,你正在开发一个智能家居系统,需要同时处理设备传感器数据和后台…...

Android开发中的蓝牙与WiFi技术深度解析:从基础到实战

引言 在移动应用开发领域,蓝牙和WiFi技术扮演着核心角色,它们支持设备间通信、数据传输和网络连接,是构建智能家居、物联网(IoT)和可穿戴设备的关键。Android平台提供了强大的API支持,使开发者能够高效集成蓝牙和WiFi功能。本文聚焦于Android开发中蓝牙和WiFi技术的应用…...

移动端开发中的蓝牙与WiFi技术深度解析与实战指南

前言:移动端开发中的无线技术核心作用 在当今移动应用生态中,无线通信技术已成为不可或缺的支柱,其中蓝牙和WiFi作为两大主流协议,驱动着物联网(IoT)、智能家居、健康监测等领域的创新。蓝牙技术(特别是低功耗蓝牙BLE)专注于短距离、低功耗设备连接,如可穿戴设备和传感…...

基于RAG与领域微调的垂直行业智能问答系统构建实践

1. 项目概述:一个专为地产与土木工程打造的智能问答助手最近在GitHub上看到一个挺有意思的项目,叫mayam2-stack/real-estate-civil-eng-chatbot。光看这个名字,就能猜到个大概:这是一个基于MayaM2技术栈,专门服务于房地…...

医药行业AI智能数据管道:自动化整合与四维评分模型解析

1. 项目概述:一个为医药行业打造的AI智能数据管道如果你在生物医药、投资或市场准入领域工作,每天花几个小时手动查询ClinicalTrials.gov、FDA数据库、PubMed和USPTO,只为拼凑出一个竞品管线的大致轮廓,那么你一定会对这个工具产生…...

魔兽争霸3终极助手:5大核心功能彻底解决经典游戏兼容性问题

魔兽争霸3终极助手:5大核心功能彻底解决经典游戏兼容性问题 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 你是否还在为魔兽争霸3在现代电…...

Dify外部知识库代理:打通Confluence、API与网页,构建动态智能助手

1. 项目概述:一个为Dify注入外部知识源的智能代理最近在折腾AI应用开发,特别是用Dify这类低代码平台快速搭建智能助手时,遇到了一个挺普遍的问题:Dify内置的知识库功能虽然方便,但很多时候我们的数据并不在本地&#x…...

别再只用pickle存数据了!用h5py管理你的PyTorch/TensorFlow模型权重(附完整代码)

深度学习模型权重管理的进阶方案:h5py实战指南 在深度学习项目的生命周期中,模型权重的存储与管理往往成为容易被忽视却至关重要的环节。当面对BERT、ResNet等参数量庞大的模型时,传统的pickle或框架原生保存方法开始暴露出诸多局限性——文件…...

别再手动调参了!用麻雀算法SSA自动优化VMD分解参数(附MATLAB代码)

基于麻雀算法的VMD参数智能优化实战:从理论到故障诊断应用 在信号处理领域,变分模态分解(VMD)因其出色的非平稳信号分析能力而广受关注。然而,传统VMD应用中最大的痛点莫过于参数选择——模态数K和惩罚因子α的确定往往依赖经验或反复试错&am…...

PTA天梯赛L2-042题保姆级攻略:用C++ STL vector和sort轻松找出老板作息表的‘摸鱼’时间

PTA天梯赛L2-042题解:用侦探思维破解老板的"摸鱼"时间 最近在PTA天梯赛的题库中,有一道关于时间区间处理的题目引起了我的注意。题目描述了一位老板在网上晒出自己的作息时间表,却被眼尖的网友发现存在时间空白。这让我想起了一个有…...

【企业级低代码内核调试SOP】:7类典型NPE/ClassDefNotFound场景对照表,含JFR采样+Arthas增强脚本

更多请点击: https://intelliparadigm.com 第一章:企业级低代码内核调试SOP概述 企业级低代码平台的内核调试并非传统应用开发的简单延伸,而是融合了元数据驱动、可视化编排、运行时沙箱与动态渲染引擎的复合型工程实践。其SOP(标…...

别光看虚拟线程了!Java 21 里这个‘字符串模板’预览特性,能让你的代码清爽一大截

别光看虚拟线程了!Java 21 里这个‘字符串模板’预览特性,能让你的代码清爽一大截 如果你是一位长期与Java打交道的开发者,最近可能被Java 21的虚拟线程(Virtual Threads)刷屏了。这个特性确实令人兴奋,但今…...

C#实战:用滚球算法搞定点云凹包,GIS和游戏地形都能用

C#实战:用滚球算法实现点云凹包,解锁GIS与游戏地形新玩法 当我们需要从一堆散乱的点数据中勾勒出它们的边界轮廓时,凸包算法往往是最先想到的解决方案。但现实世界中的形状很少是完美的凸多边形——海岸线的蜿蜒、城市边界的曲折、游戏地形的…...

避坑指南:从HuggingFace下载模型到llama.cpp量化,我踩过的那些‘坑’(含CUDA 12.2环境配置)

避坑指南:从HuggingFace下载模型到llama.cpp量化实战全解析 在部署大语言模型的过程中,从模型下载到最终量化部署,每个环节都可能隐藏着各种"坑"。本文将分享我在实际项目中积累的经验教训,特别是那些官方文档中鲜少提及…...

用Python和PySide6打造你的专属量化看盘工具:从K线到MACD的完整绘图实战

用Python和PySide6打造你的专属量化看盘工具:从K线到MACD的完整绘图实战 在量化交易的世界里,数据可视化是决策过程中不可或缺的一环。想象一下,当你需要快速验证一个交易策略的有效性,或者实时监控市场动态时,一个能够…...