借助JavaScript,实现网页动画效果html
简易Demo:es/js_animation_test.html前端
Anikyu源代码仓库:anikyugit
本人不才,大学期间搞过各类各样的设计类软件,包括作动画的Flash、After Effects、Premiere,作3D的3ds Max、Cinema 4D;终于,在大学的第三年,专业开了网页设计课(大二时其实我也有水过一个作网页的校选修),我又不慎接触了作网页的Dreamweaver。Adobe全家桶齐了。程序员
要学作网页,就得学HTML;要把网页写好看,就得学CSS;要让网页有点生气,就得学JavaScript;这不就蹚入了前端这潭浑水。github
// 其实本人并不承认本身是程序员,最可能是会写网页的设计师数据库
大学期间,本人学的是数字媒体技术专业,计算机学院里的专业,C、C++、数据结构、数据库概论、计算机图形学等课程都有上过,外加游戏原画、Flash、三维设计一类的偏向设计类的课程,而后本身又稍稍接触了一点点PHP。曾经我想,要不将来作UI/视频/3D设计师吧;但最终找工做的时候,认真思考了一下,专业课设计类课程彷佛支持不了个人一些野心,外加我比较菜,大学期间全部软件都接触很浅;到后来一直不知道3D模型贴图咋贴,就试着经过学Three.js来学3D。因此就暂时先试着用Dreamweaver这个技能来找工做吧。npm
至于设计,我认为总有一天我会回来的~segmentfault
本人认为,网页与传统动画同样,都是可以使人产生愉悦的能够用于观赏的产品;但比传统动画更强大的是,网页可以与用户直接产生交互。浏览器
专业动画设计软件里,动画的实现通常是逐帧动画或是关键帧动画。数据结构
创做者每一帧绘制一次,最终将多帧连续播放,实现动画。
创做者在每一个关键点绘制一帧,最终由程序在两个关键帧之间进行插值,获得中间帧。插值能够是线性插值,也能够是包含缓动的插值。
对于网页动画,在早期,若动画效果很复杂,甚是精美,通常都直接使用Flash等插件来制做;简单的动画则使用JavaScript中的setInterval()来实现,即每隔若干毫秒(保持性能的同时不让用户感到卡顿)对某一对象某一数值进行一次增长,请求一次动画帧,直到该对象该值的量到达预想值,由此实现动画。jQuery中的animate()对其进行了封装。同时还有SVG动画,使用animate标签来进行实现。随着CSS3的出现,DOM元素动画/过渡效果可使用CSS实现。
固然,CSS动画的使用也有一些局限性:
最近在学习Three.js,其核心库彷佛不包含动画。其官方示例有使用Tween.js这个动画库,但因为我比较想试着本身实现一个相似的动画库(实际上是由于它的用法和个人想法不太相符)。
所以,我本身封装了一个基于JavaScript实现的动画库 —— Anikyu 。
固然本文先并不着重介绍我所封装的库。对于其使用方法,请直接看anikyu的仓库。
咱们都知道,网页有两个维度:宽度和高度。
若是要用到动画,那就再加个时间。
(好吧,若是非要说WebGL或是CSS 3D等等三维技术,那再加个深度;此处不讨论)。
要让一个对象会动,就必须引入时间这个概念;由于时间点是一个点,其中包含的是对象在在当前时间点的状态;既然要让对象会动,就得找下一个时间点,最终连点成线,就产生了一个时间轴,这样就有了时间这个维度。
如今咱们把两个时间点之间的变化过程当作一个总体,开始时间点动画进度为0,结束时间点动画进度为1。
设开始时间为64,结束时间为1024,当前时间为256,那么:
总时间差 = 结束时间 - 开始时间 = 1024 - 64 = 960
当前动画进度 = (当前时间 - 开始时间)/总时间差 = (256 - 64)/960 = 0.2 ;
设开始值为222,结束值为666,那么:
当前时间点(相对于开始值)的增量
= (结束值 - 开始值)*当前动画进度
= (666 - 222)*0.2
= 88.8
所以在该时间点下:
当前对象的值 = 开始值 + 当前时间的增量 = 222 + 88.8 = 310.8
以后使用定时器执行上方的步骤,每次时间改变,从新计算一次进度和时间点增量,并赋值给目标对象,直到当前动画进度达到1,此时动画完成。
在上面的代码中,咱们已经实现了最简单的补间,即匀速补间。但在不少状况下,仅有匀速补间是不够的,例如咱们可能要实现一个动画运行先快(慢)后慢(快)/中间快(慢)两头慢(快)/稍微出去一点再回来/弹跳等诸如此类的小效果。在After Effects、Cinema 4D等设计类软件时间轴面板上,咱们能够对缓动函数进行可视化的编辑,从而咱们能够直观地看到缓动函数的曲线。
与此相似,借助CSS3所提供的transition-timing-function属性,咱们也能够直接在CSS中实现缓动动画,其属性值包括:linear、ease、ease-in、ease-out、ease-in-out,外加这五个属性值所基于的cubic-bezier(n,n,n,n) —— 三次贝塞尔曲线。
CSS3 所提供的缓动函数已经基本知足咱们平常的一些动画需求,即上文所说起的先快(慢)后慢(快)/中间快(慢)两头慢(快)/稍微出去一点再回来;但对于弹跳效果,因为CSS3 缓动动画仅支持两点之间的简单缓动,而弹跳效果在动画过程当中没法使用CSS3 所支持的三次贝塞尔曲线来表达(弹跳动画包含多个转折点),所以没法使用纯CSS来实现弹跳。
但在JavaScript中,咱们能够本身编写缓动函数。
缓动函数参见 ECharts 示例页面
In | Out | InOut |
---|---|---|
quadraticIn | quadraticOut | quadraticInOut |
cubicIn | cubicOut | cubicInOut |
quarticIn | quarticOut | quarticInOut |
quinticIn | quinticOut | quinticInOut |
sinusoidalIn | sinusoidalOut | sinusoidalInOut |
exponentialIn | exponentialOut | exponentialInOut |
circularIn | circularOut | circularInOut |
elasticIn | elasticOut | elasticInOut |
backIn | backOut | backInOut |
bounceIn | bounceOut | bounceInOut |
函数曲线示例:
若是自行百度,能够发现这些补间函数基本都是大同小异的。它们都接受一个值 当前动画播放实际进度k,返回一个通过处理的进度值 k2 用以供计算函数算出当前时间点的变化量。
假设当前动画状态以及所给定值和上一步同样,缓动函数使用的是bounceIn,则:
当前时间点(相对于开始值)的增量
= (结束值 - 开始值) * 缓动函数(当前动画进度)
= (666 - 222)*bounceOut(0.2)
= 134.31
所以在该时间点下:
当前对象的值 = 开始值 + 当前时间的增量 = 222 + 134.31 = 356.31
正如上文所说起,动画的存在依赖于时间这个维度。一个时间点表示一个时间点的状态,那如何获得动画播放期间每个时间点的状态呢?
答案就是定时器。定时器可让浏览器每间隔一段时间来执行一段函数。在早期浏览器(IE9或更低版本)中仅可以使用setInterval()来实现不断地请求动画,新版本浏览器中则引入了专门用于建立动画的requestAnimationFrame()。
requestAnimationFrame与setInterval区别在于:
Anikyu包含了requestAnimationFrame的 Polyfill,以用于支持IE9浏览器。
经过定时器不断执行下列操做:
获取到当前时间 - 将当前时间和开始时间进行一系列计算获得当前时间点状态 - 将状态赋值给原始对象
便可实现动画。
function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); }
clamp函数用于钳制数值范围。因为当前动画进度的值一定是一个[0,1]区间的值,所以咱们必须让动画进度限制在该范围内,不然可能会致使最终效果值小于/大于预期值
function bounceOut(k) { if (k < (1 / 2.75)) { return 7.5625 * k * k; } else if (k < (2 / 2.75)) { return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; } else if (k < (2.5 / 2.75)) { return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; } else { return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; } }
bounceOut函数是上文所说起的30个缓动函数之一,可在动画进度快要结束时产生弹回的效果。将当前动画进度传入后,能够得到一个通过处理的进度,在实际计算时根据该进度获得当前时间点状态,实现变速运动。若当前动画进度不通过该函数处理,则动画为匀速运动。
let el = document.getElementById('el') let init = 222; let end = 666; let timeDelta = 960; let startTime = Date.now()
function animate() { interval = requestAnimationFrame(animate) let loop = () => { let currentProgress = (Date.now() - startTime) / timeDelta currentProgress = clamp(currentProgress, 0, 1) let sumNumber = (end - init) * bounceOut(currentProgress) // let sumNumber = (end - init) * currentProgress let currentStatus = init + sumNumber if (currentProgress === 1) { cancelAnimationFrame(interval) el.style.transform = `translate(${end}px)` } else { el.style.transform = `translate(${currentStatus}px)` } } loop() } animate()
以上就是我对JavaScript动画的理解。借此机会,我也封装了一个上文所说起的动画库,Anikyu。
Anikyu是本人春节期间在家,正好也是疫情期间,作这个动画demo时忽然想封装的一个库。正巧借此机会也学习了一下ES6没用过的特性、Webpack、Babel,同时也将该库发布到了 npm 。
各位要是以为靠谱不妨来用一下,支持IE 9+浏览器,支持Node.js环境。
我也尝试将Anikyu代码结构、开发过程写一篇新的博客:
尝试经过封装一个库来学习JavaScript(ES6)相关特性以及相关构建工具]
(#7)
可能有点多,我想起来的时候慢慢补充。