跟月影学可视化
月影
前奇虎 360 奇舞团团长,可视化 UI 框架 SpriteJS 核心开发者,《JavaScript 王者归来》作者
30206 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 54 讲
跟月影学可视化
15
15
1.0x
00:00/00:00
登录|注册

18 | 如何生成简单动画让图形动起来?

实现自由落体弹性小球动画
使用缓动函数控制动画细节
标准动画模型的实现
三种动画形式及特点
应用于动画效果
实现贝塞尔曲线缓动函数
映射函数处理变速运动
使用线性插值实现匀速运动
Animator 类控制动画
Timing 类处理时间
更新动画属性
计算当前进度
定义初始时间和周期
JavaScript 实现
修改属性值
使用 CSS 实现
依次播放图像
准备静态图像
时序动画
增量动画
固定帧动画
小试牛刀
要点总结
贝塞尔曲线缓动
插值与缓动函数
定义标准动画模型
实现时序动画
实现增量动画
实现固定帧动画
三种动画形式
如何生成简单动画让图形动起来?

该思维导图由 AI 生成,仅供参考

你好,我是月影。
前面,我们用了 3 个模块的时间,学习了大量的图形学和数学知识,是不是让你的脑袋有一点昏沉?没关系,你只是需要一点时间来消化这些知识而已。我能给你的建议就是多思考、多练习,有了时间的积累,你一定可以掌握这些基础知识和思维方法。
从这一节课开始,我们要学习一个非常有意思的新模块,那就是动画和 3D 绘图。对于可视化展现来说,动画和 3D 都是用来强化数据表达,吸引用户的重要技术手段。它们往往比二维平面图形能够表达更复杂的数据,实现更吸引人的视觉效果。
那今天,我们先来聊聊动画的实现。实际上,我们之前也实现了不少动态效果,但你可能还是不知道怎么去实现动画。接下来,我们就来系统地梳理一下动画实现的标准方法。

动画的三种形式

什么是动画呢?简单来说,动画就是将许多帧静止的画面以固定的速率连续播放出来。一般来说,动画有三种形式,分别是固定帧动画、增量动画和时序动画。
第一种形式是我们预先准备好要播放的静态图像,然后将这些图依次播放,所以它叫做固定帧动画增量动画是在动态绘制图像的过程中,我们修改每一帧中某个或某几个属性的值,给它们一定的增量。第三种形式是在动态绘制图像的过程中,我们根据时间和动画函数计算每一帧中的关键属性值,然后更新这些属性,所以它叫做时序动画
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了使用HTML/CSS和JavaScript实现简单动画的方法,包括固定帧动画、增量动画和时序动画。固定帧动画适合游戏等应用场景,增量动画简单易实现但控制细节较困难,而时序动画则是最常用的方式。文章通过具体代码示例和解释,帮助读者了解这三种动画形式的基本实现原理和使用方法。此外,还介绍了插值与缓动函数的应用,以及贝塞尔曲线缓动函数的实现。通过这些内容,读者可以快速了解动画实现的基本原理和技术特点,为他们在实际项目中应用动画效果提供了有益的参考。文章还提出了一个小练习,让读者利用学到的时序动画知识,实现一个简单的弹性小球自由落体效果。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《跟月影学可视化》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(7)

  • 最新
  • 精选
  • 罗 乾 林
    最直接的想法 async function run_animator() { let _duration = 400 const start_pos = 100 const end_pos = 300 let h = end_pos - start_pos const animator = new Animator({ duration: _duration, easing: p => p ** 2 }); await animator.animate({ el: block, start: start_pos, end: end_pos }, ({ target: { el, start, end }, timing: { p, isFinished } }) => { const top = start * (1 - p) + end * p; el.style.top = `${top}px`; }); while (h > 2) { h /= 2 const top = end_pos - h _duration /= Math.sqrt(2) // console.log(`${h} ${_duration}`) const animator_up = new Animator({ duration: _duration, easing: p => p * (2 - p) }); const animator_down = new Animator({ duration: _duration, easing: p => p ** 2 }); await animator_up.animate({ el: block, start: end_pos, end: top }, ({ target: { el, start, end }, timing: { p, isFinished } }) => { const top = start * (1 - p) + end * p; el.style.top = `${top}px`; }); await animator_down.animate({ el: block, start: top, end: end_pos }, ({ target: { el, start, end }, timing: { p, isFinished } }) => { const top = start * (1 - p) + end * p; el.style.top = `${top}px`; }); } }

    作者回复: 挺好的

    2020-08-04
    4
  • 以梦为马
    老师你好,transform中Matrix是不是除了装B外就没什么用呀.... 一直没发现它的用武之地

    作者回复: matrix可以更加灵活设置transform,背后原理就是前面讲的仿射变换

    2020-08-22
  • 量子蔷薇
    // 小试牛刀,这里给出关键代码,函数 fall: async function fall(h, duration, k = 0.5) { await new Animator({ duration, easing: p => p ** 2 }).animate(circle, ({ target, timing }) => { target.y = h * (1 - timing.p); }); const nextH = h * k; const nextDuration = (duration ** 2 * k) ** 0.5; if (nextDuration > 16) { await new Animator({ duration: nextDuration, easing: p => p * (2 - p) }).animate(circle, ({ target, timing }) => { target.y = nextH * timing.p; }); fall(nextH, nextDuration, k); } } // h 为下落高度,duration 为持续时间,k 是衰减系数,默认每次弹起后高度减半 // 利用了递归的思想,当 nextDuration 小于16(60fps 的帧间隔是16.666…)时停止弹起
    2022-10-29归属地:上海
    1
  • KaygNas
    请问月影大大,将线性的 f(p) = p 映射成任意的函数有更通用的求解方式吗?
    2023-06-05归属地:广东
  • 我叫张小咩²⁰¹⁹
    import {Animator} from '../common/lib/animator/index.js'; const ball = document.querySelector('.ball'); (async function () { let start = 0; let end = 200; let _duration = 1000 while(start < 200) { // eslint-disable-next-line no-await-in-loop const animeFall = new Animator({duration: _duration, easing: p => p ** 2 }); await animeFall.animate(ball, ({target, timing: { p }}) => { const top = start * (1 - p) + end * p; target.style.top = `${top}px`; }); start = Math.ceil((end - start) / 2 + start) _duration /= Math.sqrt(2) const animeBound = new Animator({duration: _duration, easing: p => p * (2 - p) }); await animeBound.animate({ el: ball, start: end, end: start }, ({target: { el, start, end }, timing: { p }}) => { const top = start * (1 - p) + end * p; el.style.top = `${top}px`; }); console.log(start, end) } }());
    2023-05-19归属地:北京
  • 林逸舟
    const ball = document.querySelector('.ball') const downAnimator = new Animator({ duration: 700, iterations: 1, easing: p => p ** 2 }) const upAnimator = new Animator({ duration: 700, iterations: 1, easing: p => p * (2 - p) }) setTimeout(async () => { let start = 0; let end = 200; let T = 200; while (Math.round(start) !== end) { await downAnimator.animate({ el: ball, start, end }, (({ target: { el, start, end }, timing }) => { const { p } = timing const top = start * (1 - p) + end * p el.style.top = `${top}px` })) start += T /= 2; await upAnimator.animate({ el: ball, start: end, end: start }, (({ target: { el, start, end }, timing }) => { const { p } = timing const top = start * (1 - p) + end * p el.style.top = `${top}px` })) } }, 1000);
    2022-02-07
  • MG.Fre
    // p = p ** 2; // 匀加速运动 // p = p * (2 - p); // 匀减速运动 let start = 0, end = 200, duration = 800; let distance = end - start; const animator = new Animator({ duration, easing: p => p ** 2 }); document.addEventListener('click', async () => { while(Math.abs(distance) >= 0.1){ // 根据方向更新动画函数 if(distance > 0) // 向下运动,匀加速 animator.updateTiming({easing: p => p ** 2}); else // 向上运动,匀减速 animator.updateTiming({easing: p => p * (2 - p)}); // 更新动画的周期时间(保持加速度恒定) animator.updateTiming({duration: duration *= 0.7}); await animator.animate({el: block, start, end}, ({target: {el, start, end}, timing: {p}}) => { const top = start * (1 - p) + end * p; el.style.top = `${top}px`; }); // 通过distance记录运动方向 distance = - distance / 2; // 更新开始与结束的位置变化 if(distance < 0){ [start, end] = [end, end + distance]; }else{ [start, end] = [end, start]; } } });
    2021-08-10
收起评论
显示
设置
留言
7
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部