18 | 如何生成简单动画让图形动起来?
月影
该思维导图由 AI 生成,仅供参考
你好,我是月影。
前面,我们用了 3 个模块的时间,学习了大量的图形学和数学知识,是不是让你的脑袋有一点昏沉?没关系,你只是需要一点时间来消化这些知识而已。我能给你的建议就是多思考、多练习,有了时间的积累,你一定可以掌握这些基础知识和思维方法。
从这一节课开始,我们要学习一个非常有意思的新模块,那就是动画和 3D 绘图。对于可视化展现来说,动画和 3D 都是用来强化数据表达,吸引用户的重要技术手段。它们往往比二维平面图形能够表达更复杂的数据,实现更吸引人的视觉效果。
那今天,我们先来聊聊动画的实现。实际上,我们之前也实现了不少动态效果,但你可能还是不知道怎么去实现动画。接下来,我们就来系统地梳理一下动画实现的标准方法。
动画的三种形式
什么是动画呢?简单来说,动画就是将许多帧静止的画面以固定的速率连续播放出来。一般来说,动画有三种形式,分别是固定帧动画、增量动画和时序动画。
第一种形式是我们预先准备好要播放的静态图像,然后将这些图依次播放,所以它叫做固定帧动画。增量动画是在动态绘制图像的过程中,我们修改每一帧中某个或某几个属性的值,给它们一定的增量。第三种形式是在动态绘制图像的过程中,我们根据时间和动画函数计算每一帧中的关键属性值,然后更新这些属性,所以它叫做时序动画。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了使用HTML/CSS和JavaScript实现简单动画的方法,包括固定帧动画、增量动画和时序动画。固定帧动画适合游戏等应用场景,增量动画简单易实现但控制细节较困难,而时序动画则是最常用的方式。文章通过具体代码示例和解释,帮助读者了解这三种动画形式的基本实现原理和使用方法。此外,还介绍了插值与缓动函数的应用,以及贝塞尔曲线缓动函数的实现。通过这些内容,读者可以快速了解动画实现的基本原理和技术特点,为他们在实际项目中应用动画效果提供了有益的参考。文章还提出了一个小练习,让读者利用学到的时序动画知识,实现一个简单的弹性小球自由落体效果。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《跟月影学可视化》,新⼈⾸单¥68
《跟月影学可视化》,新⼈⾸单¥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-044 - 以梦为马老师你好,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
收起评论