讲师中心 微信公众号
AI工具推荐 视频效率加速

掌握 JavaScript 缓动函数:实现精确动画时序

夏强大大_9740

夏强大大_9740

发布时间:2025-10-29 11:01:02

|

287人浏览过

|

来源于php中文网

原创

掌握 JavaScript 缓动函数:实现精确动画时序

本文深入探讨在 javascript 动画中使用缓动函数时,如何正确处理时间参数。核心问题在于动画起始时间的管理,而非简单使用全局时间戳。通过记录动画的起始时间并计算相对时间,结合 `requestanimationframe`,可以实现精确且可控的动画效果,避免动画跳跃或行为异常。

在 Web 开发中,缓动函数(Easing Functions)是实现流畅自然动画效果的关键。它们允许动画在开始、中间或结束时加速或减速,而非简单地线性变化。然而,正确使用缓动函数,特别是其时间参数 t,对于新手来说常常是一个挑战。许多开发者可能会误用全局时间戳,导致动画在非预期时间启动时出现跳跃或不连续的问题。

理解缓动函数的核心参数

在深入探讨解决方案之前,我们首先回顾一下标准缓动函数通常接受的四个核心参数:

  • t (time):当前时间。这通常是动画从开始到当前帧经过的时间。
  • b (beginning value):动画起始值。例如,如果一个元素从 x=0 开始移动,b 就是 0。
  • c (change in value):动画总的变化量。例如,如果一个元素从 0 移动到 100,c 就是 100。
  • d (duration):动画的总持续时间。

缓动函数会根据这四个参数计算出动画在当前时间点 t 应该达到的值。例如,一个简单的线性缓动函数可能是 c * t / d + b。

动画时序的常见误区

一个常见的错误是直接将 performance.now()(或类似的时间戳)作为缓动函数的 t 参数。performance.now() 返回的是自页面加载以来经过的毫秒数,这是一个全局的、不断增长的时间戳。如果动画在代码执行的早期启动,这可能看起来正常,因为 t 从一个相对较小的值开始。但如果动画在页面加载后一段时间才触发(例如,用户点击按钮后),此时 performance.now() 已经是一个较大的值,直接将其作为 t 传入会导致缓动函数立即计算出一个较大的结果,使动画“跳跃”到中间或结束状态,而不是从起始值 b 开始。

立即学习Java免费学习笔记(深入)”;

问题的根源在于,缓动函数中的 t 参数需要的是动画相对于其自身起始点的运行时间,而不是页面加载以来的总运行时间。

解决方案:管理动画起始时间

要解决这个问题,核心在于为每个动画实例记录其独立的起始时间。当动画开始时,我们应该捕获当前的 performance.now() 值作为该动画的 startTime。然后,在动画的每一帧中,我们计算当前时间与 startTime 之间的差值,这个差值就是动画的实际运行时间 animTime,它将作为缓动函数的 t 参数。

Canvas 内容工厂(网格生成→多页 PDF→SVG 精准复刻)
Canvas 内容工厂(网格生成→多页 PDF→SVG 精准复刻)

用户要生成可打印的中文字帖/练习纸、导出多页 A4 PDF 报告,或把 SVG 设计稿零误差还原到 Canvas 时使用。本技能是「Canvas 内容工厂闭环」的总控,编排:网格渲染引擎(13 种教育网格+拼音标注) → 多页 PDF 导出(A4 合成) → SVG 精准复刻(坐标误差<0.001px)。触发词:生成字帖、练习纸、导出 PDF、SVG 转 Canvas、印刷级还原、A4 报告、米字格田字格。

下载

其计算公式如下:

animTime = performance.now() - startTime;

通过这种方式,无论动画何时启动,其 animTime 总是从 0 开始递增,直到达到动画的总持续时间 d。

实战示例:使用缓动函数创建动画

以下示例演示了如何在 JavaScript 中正确使用缓动函数来控制 Canvas 上的图形动画。我们将在用户点击 Canvas 时启动动画,并使用 requestAnimationFrame 来确保动画流畅。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript 缓动函数教程</title>
    <style>
        canvas {
            border: 1px solid black;
            display: block; /* 避免 canvas 底部有空白 */
            margin: 20px auto; /* 居中显示 */
        }
        body {
            font-family: Arial, sans-serif;
            text-align: center;
        }
        p {
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <p>点击 Canvas 区域以启动或重新启动动画。</p>
    <canvas id="animationCanvas" width="500" height="200"></canvas>

    <script>
        // 缓动函数定义 (来自 https://spicyyoghurt.com/tools/easing-functions)
        const easeLinear = (t, b, c, d) => c * t / d + b;
        const easeInOutQuad = (t, b, c, d) => (t /= d * 0.5) < 1 ? c * 0.5 * t * t + b : -c * 0.5 * ((t - 1) * (t - 3) - 1) + b;

        const canvas = document.getElementById("animationCanvas");
        const ctx = canvas.getContext("2d");

        let animating = false; // 标记动画是否正在进行
        let startTime;         // 动画的起始时间戳
        const animDuration = 2000; // 动画总持续时间 (毫秒)

        // 初始绘制,确保 Canvas 不为空
        function drawInitialState() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.beginPath();
            ctx.arc(20, 20, 20, 0, Math.PI * 2); // 初始位置
            ctx.fill();
        }
        drawInitialState(); // 页面加载时绘制一次

        // 监听 Canvas 点击事件,启动动画
        canvas.addEventListener("click", () => {
            startTime = performance.now(); // 记录动画开始时间
            if (!animating) { // 如果当前没有动画在运行,则启动动画循环
                requestAnimationFrame(mainLoop);
                animating = true; // 设置动画状态为正在进行
            }
        });

        // 动画主循环
        function mainLoop(currentTime) {
            ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除上一帧

            if (startTime !== undefined) { // 确保动画已启动
                // 计算动画的相对运行时间
                const animTime = currentTime - startTime;

                // 使用缓动函数计算当前帧的 x 和 y 坐标
                // x 轴:从 -20 (左侧外) 移动到 canvas.width + 20 (右侧外),总变化量 canvas.width + 40
                const x = easeLinear(animTime, -20, canvas.width + 40, animDuration);
                // y 轴:从 20 移动到 canvas.height - 20,总变化量 canvas.height - 40
                const y = easeInOutQuad(animTime, 20, canvas.height - 40, animDuration);

                // 绘制圆形
                ctx.beginPath();
                ctx.arc(x, y, 20, 0, Math.PI * 2);
                ctx.fill();

                // 判断动画是否应该继续
                if (animTime < animDuration) {
                    // 如果动画未结束,请求下一帧
                    requestAnimationFrame(mainLoop);
                } else {
                    // 动画结束,重置状态
                    animating = false;
                    startTime = undefined; // 清除起始时间,以便下次点击重新开始
                    // 绘制最终状态,确保动画结束后停留在正确位置
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    ctx.beginPath();
                    ctx.arc(
                        easeLinear(animDuration, -20, canvas.width + 40, animDuration),
                        easeInOutQuad(animDuration, 20, canvas.height - 40, animDuration),
                        20, 0, Math.PI * 2
                    );
                    ctx.fill();
                }
            }
        }
    </script>
</body>
</html>

在上述代码中:

  1. startTime 变量: 用于存储动画开始时的 performance.now() 值。每次点击 Canvas 启动新动画时,它都会被更新。
  2. animTime 计算: 在 mainLoop 函数中,animTime = currentTime - startTime; 确保了缓动函数接收到的 t 参数是动画从 0 开始计时的相对时间。
  3. 动画循环控制: if (animTime < animDuration) 条件判断动画是否仍在进行。如果 animTime 超过 animDuration,则动画结束,animating 标志被重置,并且不再请求下一帧。

关键注意事项

  • requestAnimationFrame: 始终使用 requestAnimationFrame 来驱动 Web 动画。它能确保动画在浏览器渲染周期的最佳时机执行,避免卡顿,并节省电量。
  • 动画状态管理: 使用一个布尔标志(如 animating)来跟踪动画是否正在运行,可以防止重复启动动画或在动画进行时重置状态。
  • 多动画实例: 如果需要同时运行多个独立的动画,每个动画都需要有自己的 startTime 和 animating 状态变量,或者封装在一个动画管理器类中。
  • 动画结束处理: 动画结束后,确保清除 startTime 或将动画状态重置,以便下次能够重新启动。同时,可能需要绘制动画的最终状态,以避免在清除 Canvas 后元素消失。

总结

正确使用 JavaScript 缓动函数的核心在于精确管理动画的起始时间。通过记录动画独立的 startTime,并计算出 animTime = currentTime - startTime 作为缓动函数的 t 参数,我们可以确保动画无论何时启动都能从预期位置平滑开始,避免跳跃。结合 requestAnimationFrame 的使用,这种方法能帮助我们构建高性能、视觉流畅且易于控制的 Web 动画。

热门AI工具

更多
蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
html5动画制作有哪些制作方法
html5动画制作有哪些制作方法

html5动画制作方法有使用CSS3动画、使用JavaScript动画库、使用HTML5 Canvas等。想了解更多html5动画制作方法相关内容,可以阅读本专题下面的文章。

3371

2023.10.23

canva可画使用技巧汇总
canva可画使用技巧汇总

本专题汇集了PHP中文网精心整理的Canva可画新手使用技巧大全,专为零基础用户打造。无论你是想制作精美海报、PPT还是社交媒体配图,这里都有从注册登录、模板搜索到AI辅助设计、导出分享的全流程保姆级教程。通过深入浅出的图文指南,帮助你快速跨越设计门槛,轻松掌握Canva可画的实用操作,让每个人都能成为设计达人!

204

2026.05.19

AI视频生成软件推荐
AI视频生成软件推荐

本专题汇总了当前主流的AI视频生成软件推荐与排行榜单,涵盖seko、AniShort、剧云、Lovart、LiblibAI及立刻mv等热门工具。同时整理了各软件在文生视频、图生视频、时长限制、画质表现及免费额度等方面的差异对比,助您快速选对适合创作需求的AI视频生成工具。

120

2026.09.16

ai生成视频的工具免费版合集
ai生成视频的工具免费版合集

本专题汇总了当前免费AI生成视频工具的排行榜与推荐清单,涵盖seko、讯飞智作、AniShort及剧云、Lovart等多模型集成平台。同时整理了各工具的免费额度、输出时长、水印政策及适用场景差异,助您快速选择合适工具开启AI视频创作。

40

2026.09.16

Pandas时间序列分析与可视化报表
Pandas时间序列分析与可视化报表

本专题整理Pandas日期转换、时间索引、重采样、滚动窗口、时区处理、plot绘图、Styler表格样式和报表输出方法。

40

2026.09.16

Pandas数据筛选索引与清洗处理
Pandas数据筛选索引与清洗处理

本专题整理Pandas中的loc、iloc、条件筛选、query查询、缺失值处理、重复值删除、类型转换和字符串列清洗方法。

40

2026.09.16

Pandas数据读取导入与文件导出处理
Pandas数据读取导入与文件导出处理

本专题整理Pandas读取CSV、Excel、JSON、SQL、Parquet等文件的方法,以及to_csv、to_excel、to_sql和to_parquet等常用数据导出流程。

40

2026.09.16

GDB怎么设置断点
GDB怎么设置断点

本专题介绍GDB按照函数名、源代码行号和文件位置设置断点的方法,详细说明run、continue、next、step等命令的配合使用,帮助定位程序崩溃、逻辑异常及代码未按预期执行的问题。

340

2026.09.11

GDB怎么查看变量值
GDB怎么查看变量值

本专题介绍GDB调试过程中查看变量值的具体方法,涵盖局部变量、函数参数、数组、结构体和指针内容查询,同时整理变量持续显示、格式化输出及无法读取变量时的排查思路。

100

2026.09.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 11.8万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn