
文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载导读本文围绕 现代 JavaScript 教程 中「JavaScript 动画」一节的经典练习「为弹跳的球设置动画」展开完整解析 任务描述 与 官方解决方案如何用position: absolute与top属性驱动小球下落如何借助bounce时序函数配合makeEaseOut变换实现逼真的落地反弹。读完本文你将掌握animate({timing, draw, duration})通用动画函数的使用方式、球体落地坐标的计算方法以及easeOut变换让反弹从开头移到结尾的核心原理并能在真实项目中复现这一效果。一、任务回顾做一个弹跳的球在 任务文件 中练习目标非常明确实现一个点击后开始弹跳的球。任务的原始描述只有一行需求与一个可交互的预览做一个弹跳的球。点击查看应有的效果。从仓库的 source.view 初始代码 可以看到练习给出的 HTML 结构非常简洁div idfield img srchttps://js.cx/clipart/ball.svg width40 height40 idball /div配套的 style.css 定义了两个关键样式#field { height: 200px; border-bottom: 3px black groove; position: relative; } #ball { position: absolute; cursor: pointer; }这里有两个值得注意的细节#field使用position: relative这是小球能以position: absolute定位的锚点。只有父容器是相对定位小球的top值才会以field区域为参照系计算。#ball使用position: absolute小球脱离文档流可以自由地通过top/left属性移动。border-bottom: 3px black groove给场地画出一条地面线让小球落在底部时视觉上形成落地效果。二、解题思路坐标计算是动画的前提官方解决方案 的第一步是确定小球的运动范围。这是整个动画的数学基础为了达到反弹效果我们可以在带有position:relative属性的区域内给小球使用top和position:absoluteCSS 属性。field区域的底部坐标是field.clientHeight。top属性给出了球顶部的坐标在最底部时达到field.clientHeight - ball.clientHeight。因此我们将top从0变化到field.clientHeight - ball.clientHeight来设置动画。推导逻辑如下field.clientHeight场地的内容高度200px即 CSS 中设置的height: 200px。ball.clientHeight小球自身的高度40px由img width40 height40决定。top表示小球顶部的坐标当top 0时球位于场地最顶端当top field.clientHeight - ball.clientHeight时球顶部贴着场地底部即小球落地。之所以要减去ball.clientHeight是因为绝对定位的top定位的是元素上边缘。如果不减去球高小球底部就会超出场地 40px看起来陷进地里。这一减法是绝对定位动画中极易遗漏、却又至关重要的细节。因此动画的top取值范围为top 从 0 变化到 field.clientHeight - ball.clientHeight即 200 - 40 160px三、时序函数从下落到弹跳如果只把top从 0 线性变化到 160小球只会匀速下落毫无弹跳感。弹跳效果的关键在于时序函数timing function。在 「JavaScript 动画」主教程 中通用动画函数animate接受三个参数duration动画总时长毫秒。timing(timeFraction)时序函数接收已过去时间与总时长的比值0 到 1返回动画进度通常也是 0 到 1。draw(progress)根据进度值实际绘制画面的函数。3.1 bounce 时序函数模拟球的反弹教程中给出了标准的bounce时序函数function bounce(timeFraction) { for (let a 0, b 1, result; 1; a b, b / 2) { if (timeFraction (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) Math.pow(b, 2) } } }这个函数用for(;;)无限循环配合二分式的b / 2构造出多段抛物线bounce输出的进度会在 0~1 区间内呈现下落-弹起-再下落-再弹起……的波浪形态数学上等价于抛球落地后的连续反弹轨迹。每次循环a b, b / 2反弹的高度系数逐次减半最终收敛到 1即动画结束时小球稳稳落地。3.2 makeEaseOut 变换把反弹挪到动画末尾如果直接使用bounce小球会一开始就在原地弹跳这与球先落下再反弹的直觉相反。解决方案是 solution.md 中点名的easeOut模式现在为了获得弹跳效果我们可以在easeOut模式下使用时序函数bounce。makeEaseOut是一个高阶函数包装器把任意时序函数反转function makeEaseOut(timing) { return function(timeFraction) { return 1 - timing(1 - timeFraction); } }其数学含义为timingEaseOut(timeFraction) 1 - timing(1 - timeFraction)时间轴被翻转1 - timeFraction进度轴也被翻转1 - ...。效果是原时序函数开头的行为被搬到了动画末尾。于是原始bounce动画一开始就弹跳最后突然跳到顶部。makeEaseOut(bounce)小球先落下在动画结束时才呈现弹跳视觉上就是球落地后反弹。下面的对比图展示了常规bounce红色与makeEaseOut(bounce)蓝色的行为差异从图中可以直观看到常规弹跳是底部弹跳然后突然跳到顶部而easeOut变换后是先跳到顶部之后在那里弹跳这正是弹跳球动画所需要的时序。四、完整解决方案坐标 × 时序 × 绘制将以上两部分组合就得到 solution.md 给出的最终动画代码let to field.clientHeight - ball.clientHeight; animate({ duration: 2000, timing: makeEaseOut(bounce), draw(progress) { ball.style.top to * progress px } });逐行解读let to field.clientHeight - ball.clientHeight;预先算出小球的落地位置160px动画期间无需重复读取 DOM 尺寸。duration: 2000动画总时长 2 秒球从落地到弹跳结束一气呵成。timing: makeEaseOut(bounce)把bounce时序函数包进makeEaseOut让反弹发生在动画末尾。draw(progress)progress从 0 增长到 1ball.style.top则从0px变化到160px小球随之从顶部下落到地面并完成反弹。值得强调的是draw与timing的分工timing决定什么时候该走到哪一步非线性包含反弹draw只负责把抽象的进度值线性映射为实际的 CSS 属性值。这种解耦正是animate通用框架的精髓也是本教程反复强调的可以设计任何时序函数和任何绘图函数的体现。五、源码级验证solution.view 的完整实现仓库中的 solution.view/index.html 是可直接运行的完整解答其中包含了makeEaseOut、bounce两个函数的完整定义并通过ball.onclick绑定点击触发动画script function makeEaseOut(timing) { return function(timeFraction) { return 1 - timing(1 - timeFraction); } } function bounce(timeFraction) { for (let a 0, b 1, result; 1; a b, b / 2) { if (timeFraction (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) Math.pow(b, 2) } } } ball.onclick function() { let to field.clientHeight - ball.clientHeight; animate({ duration: 2000, timing: makeEaseOut(bounce), draw(progress) { ball.style.top to * progress px } }); }; /script这里有两个上下文要点animate函数从外部引入页面通过script srchttps://js.cx/libs/animate.js/script加载了教程配套的动画库。该库的核心实现就是 article.md 中完整给出的通用animate函数以requestAnimationFrame为基础计算timeFraction并逐帧调用timing与draw。在本地复现时可以直接把教程中的animate定义粘贴进来。ball.onclick是触发入口ball元素带有cursor: pointer样式见 style.css提示用户点击小球即可播放动画每次点击都会重新启动一次完整的下落-反弹过程。六、底层原理animate 如何驱动每一帧要真正理解这个方案需要知道animate内部发生了什么。教程在 「JavaScript 动画」章节 给出了完整实现function animate({timing, draw, duration}) { let start performance.now(); requestAnimationFrame(function animate(time) { // timeFraction 从 0 增加到 1 let timeFraction (time - start) / duration; if (timeFraction 1) timeFraction 1; // 计算当前动画状态 let progress timing(timeFraction); draw(progress); // 绘制 if (timeFraction 1) { requestAnimationFrame(animate); } }); }动画循环的四个关键环节记录起点performance.now()保存动画开始时刻。计算时间进度timeFraction (time - start) / duration从 0 线性增长到 1超过 1 时钳制为 1。计算动画进度progress timing(timeFraction)本例中即makeEaseOut(bounce)(timeFraction)输出带反弹的进度曲线。绘制与续帧draw(progress)更新ball.style.top只要timeFraction 1就继续请求下一帧。requestAnimationFrame的好处是回调会在浏览器准备重绘时执行多个动画共享同一次重绘页面切到后台时重绘暂停动画自动停止且不消耗资源——这正是弹跳球这类纯前端动画推荐使用requestAnimationFrame而非setInterval的原因。七、扩展思路让球边弹边跳理解了弹跳球的核心方案后还可以在仓库中找到它的进阶版本。在 2-animate-ball-hops/solution.view/index.html 中同一个bounce makeEaseOut方案被扩展为同时运行两个动画一个负责垂直方向的弹跳另一个负责水平方向的前进let height field.clientHeight - ball.clientHeight; let width 100; animate({ duration: 2000, timing: makeEaseOut(bounce), draw: function(progress) { ball.style.top height * progress px } }); animate({ duration: 2000, timing: makeEaseOut(quad), draw: function(progress) { ball.style.left width * progress px } });这个进阶版演示了两个重要事实多个动画可以并行两个独立的animate调用都基于requestAnimationFrame浏览器会把它们的重绘合并为一次性能开销可控。draw可以是任意映射垂直方向用makeEaseOut(bounce)实现弹跳水平方向用makeEaseOut(quad)二次方时序实现匀速加速的滚动两者互不干扰。这印证了教程的结论draw不局限于操作 CSS 属性时序函数也不局限于 Bezier 曲线。八、总结与要点速查弹跳球动画虽然代码不足十行却浓缩了现代 JavaScript 动画的三个核心知识点知识点关键内容出处坐标计算top从0变化到field.clientHeight - ball.clientHeightsolution.md时序函数bounce用分段抛物线模拟落地反弹b / 2逐次衰减反弹幅度article.md变换函数makeEaseOut(timing) 1 - timing(1 - timeFraction)把反弹从开头搬到末尾solution.md、article.md实现弹跳球的完整步骤回顾父容器#field设置position: relative小球#ball设置position: absolute。计算落地坐标to field.clientHeight - ball.clientHeight。定义或引入bounce时序函数与makeEaseOut包装器。调用animate({ duration, timing: makeEaseOut(bounce), draw })在draw中更新ball.style.top。用ball.onclick配合cursor: pointer作为交互触发入口。需要本地运行时只需把 solution.view/index.html 中的makeEaseOut、bounce以及教程中的animate定义复制到自己的 HTML 页面并保证 HTML 结构与 CSS 样式一致即可。赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐Modern JavaScript Tutorial 实战用 requestAnimationFrame 与 easeOut(bounce) 时序实现弹跳球动画Modern JavaScript Tutorial 实战用 requestAnimationFrame 与 easeOut bounce 时序实现弹跳球动画文档/教程前端用 requestAnimationFrame 实现弹跳球动画现代 JavaScript 教程 JS 动画实战解析用 requestAnimationFrame 实现弹跳球动画现代 JavaScript 教程 JS 动画实战解析 本文以《现代 JavaScript 教程》文档/教程前端让球在球场中移动现代 JavaScript 教程 click 事件坐标换算实战让球在球场中移动现代 JavaScript 教程 click 事件坐标换算实战 点击球场任意位置让球平滑地飞到鼠标指针下方、且始终不越过场地边界——这是文档教程前端上一篇Kornia normalize_quaternion float16 数值修复解析eps 下限与除法保护机制的实现原理下一篇Rust By Example 实战用 Child::wait 同步等待子进程结束并获取 ExitStatus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考