抱歉,你查看的文章已删除

大话 JavaScript 动画

背景

138.2亿年前,世界上没有时间和空间,或许世界都不存在,在一个似有似无的点上,汇集了所有的物质,它孕育着无限的能量与可能性。

宇宙大爆炸

巨大的内力已无法被抑制,瞬间爆发,它爆炸了!世界上有了时间和空间,随着岁月的变迁,时光的流逝,无数的星系、恒星、卫星、彗星形成。我们生活的地球,只是茫茫宇宙中的一个小小的天体,或许在遥远的宇宙的另一边,会有平行世界的存在,或许在那里,我们可能是医生、老师、公务员。科学家说我们的宇宙正在加速度的膨胀,暗能量在无限吞噬着暗物质,未来的世界将会变得虚无缥缈。

人类起源

宇宙的形成,带来了无限可能性,人类释放着欲望和克制,对宇宙的渴望产生于公元前五世纪,古巴比伦人通过观察天体的位置以及外观变化,来预测人世间的各种事物。在遥远的古罗马,人们也舞弄着灵魂,把不羁的想象赋予肉体。Anim,来源于拉丁语,代表着灵魂与生命,代表着所有与生俱来。似乎世间万物都存在联系,宇宙、自然都存在灵魂。

动画的形成

两万五前年钱的石器时代,石洞中的野兽奔跑分析图,这是人类开始试图捕捉动作最早证据。文艺复兴时期的达芬奇画作上,用两只手臂两条腿来标识上下摆动的动作,在一张画作上做出不同时间的两个动作。 直到1906年,世界上第一部动画片《滑稽脸的幽默相》问世。

所以动画是否就是将多个画面连起来播放呢?

时间是连续的吗?是可以无线分割的吗?我也不太清楚,你看到的流星、人们的动作是连续的吗?或许是吧,毕竟现实生活中还没有像瞬间移动这种事情发生吧。

神经可能不是连续的,生物课学过,神经的传递是一个电信号传递过程,并且是颗粒的(神经信号),那么我们看到的东西在我们脑海里的成像一定不是连续的。

那么我们为什么能看到连续的动作呢?

视觉暂留(Persistence of vision),让我们看到了连续的画面,视神经反应速度大约为1/16秒,每个人不太一样,有些人高一点,一些人低一点。上一次视神经传递的图像将会在大脑中存留,直到下一次神经信号到达。维基百科上说,日常用的日光灯每秒钟大约会熄灭100次,但是你并没有感觉。

一般电影的在帧率在24FPS以上,一般30FPS以上大脑会认为是连贯的,我们玩的游戏一般在30FPS,高帧率是60FPS。

小时候一定看过翻页动画吧,可以看一看翻页动画-地球进化史

前端动画实现

Atwood 定律:Any application that can be written in JavaScript will eventually be written in JavaScript.

前端做动画不是什么新鲜事了,从jQuery时代,到当下,无不是前端动画横行的时代。

我们知道多张不同的图像连在一起就变成了动态的图像。

在前端的世界里,浏览器在视觉暂留时间内,连续不断的逐帧输出图像。每一帧输出一张图像。

提及动画一定会讨论到帧率(FPS, Frame Per Second),代表每秒输出帧数,也就是浏览器每秒展示出多少张静态的图像。

DOM动画中的 CSS3

CSS3 动画是当今盛行的 Web 端制作动画的方式之一,对于移动设备来说覆盖率已经非常广泛,在日常开发中可以使用。CSS3 动画只能通过对 CSS 样式的改变控制 DOM 进行动画

DOM动画中的 WebAnimation

WebAnimation 还在草案阶段,在Chrome可以尝试使用一下。移动设备还是相当惨烈,iOS 并没有开始支持。

Element Animation MDN

CSS3 和 WebAnimation 都只能作用于DOM,那么,如果我们想让 Canvas 上的对象产生动画,那我们该怎么办呢?

JavaScript

既然我们知道动画的原理,其实就是让用户看到连续的图片,并且每一张图片是有变化的。

对于事物来讲,我们可以通过改变某些数值来修改他的属性,从来改变他的外在展示。比如正方形的边长,颜色的RGB值,台风的位置(世界坐标),在每一帧去改变这些数值,根据这些数值将对象绘依次制到屏幕上,将会产生动画。

通过上面的描述,我们知道,实现一个动画,其实是数值随时间变化,以帧为时间单位。

在很久很久以前,JavaScript 使用 setInterval 进行定时调用函数。所以可以使用setInterval来进行数值的改变。

为了更好的让各位前端小哥哥小姐姐们做动画,出现了requestAnimationFramerequestAnimationFrame 接收一个函数,这个函数将在下一帧渲染之前执行,也就是说,不需要太多次的计算,只要在下一帧渲染之前,我们将需要修改的数值修改掉即可。requestAnimationFrame 的帧率和硬件以及浏览器有关,一般是60FPS(16.66666666ms/帧)。

我们利用 Dom 进行动画的演示~

元素移动

创建一个方块

<div class=“box”></div>

设置宽高和背景颜色

.box {
    width: 100px;
    height: 100px;
    background: red;
}
const box = document.querySelector('.box') // 获取方块元素
let value = 0 // 设置初始值
// 创建每一帧渲染之前要执行的方法
const add = () => {
    requestAnimationFrame(add) // 下一帧渲染之前继续执行 add 方法
    value += 5 // 每帧加数值增加5
    box.style.transform = `translateX(${value}px)` // 将数值设置给 方块 的 css 属性 transform 属性可以控制元素在水平方向上的位移
}
requestAnimationFrame(add) // 下一帧渲染之前执行 add 方法

这样,方块每帧向右移动 5 像素,每秒移动60*5=300像素,不是每秒跳动一下,而是一秒在300像素内均匀移动哦。

补间动画

上一个demo实现了小方块从左到右的移动,但是貌似他会永无止境的移动下去,直到数值溢出,小时候学过flash的朋友都知道补间动画,其实就是让小方块0px到300px平滑移动。其实就是固定的时间点,有固定的位置。

所以我们只需要根据运动的已过时间的百分比去计算数值。

保持之前的 HTML 和 CSS 不变

/**
 *  执行补间动画方法
 *
 * @param      {Number}    start     开始数值
 * @param      {Number}    end       结束数值
 * @param      {Number}    time      补间时间
 * @param      {Function}  callback  每帧的回调函数
 */
function animate(start, end, time, callback) {
    let startTime = performance.now() // 设置开始的时间戳
    let differ = end - start // 拿到数值差值
    // 创建每帧之前要执行的函数
    function loop() {
        raf = requestAnimationFrame(loop) // 下一阵调用每帧之前要执行的函数
        const passTime = performance.now() - startTime // 获取当前时间和开始时间差
        let per = passTime / time // 计算当前已过百分比
        if (per >= 1) { // 判读如果已经执行
              per = 1 // 设置为最后的状态
              cancelAnimationFrame(raf) // 停掉动画
        }
        const pass = differ * per // 通过已过时间百分比*开始结束数值差得出当前的数值
        callback(pass) // 调用回调函数,把数值传递进去
    }
    let raf = requestAnimationFrame(loop) // 下一阵调用每帧之前要执行的函数
}

我们调用一下补间动画,让数值经过1秒匀速从0变成400。

let box = document.querySelector()
animate(0, 400, 1000, value => {
    box.style.transform = `translateX(${value}px)` // 将数值设置给 方块 的 css 属性 transform 属性可以控制元素在水平方向上的位移
})

一个简单的匀速补间动画就这么被我们做好了。

非匀速动画

那万一,这个动画不是非匀速的,比如抖一抖啊,弹一弹,那该怎么办呢?

当然也是一样,根据已过时间的百分比去计算数值

时间是匀速的,但是数值不是,如果数值变化是有规律的,那么我们就可以使用时间来表示数值,创建一个接收时间比例(当前时间百分比),返回当前位置比例(当前位置百分比)的方法。

我们称这个方法叫做缓动方法。

如果速度从慢到快,我们可以把时间和数值的图像模拟成以下的样子。

公式为 rate = time ^ 2

对应的函数应该是

function  easeIn(time) { // 接收一个当前的时间占总时间的百分比比
    return time ** 2
}

这个实现加速后抖动结束的效果,在Time小于0.6时是一个公式,time大于0.6是另外一个公式。

Time < 0.6 时: Rate = Time / 0.6 ^ 2

Time > 0.6 时: Rate = Math.sin((Time-0.6) ((3 Math.PI) / 0.4)) * 0.2 + 1

最终实现的函数是

function shake(time) {
    if (time < 0.6) {
        return (time / 0.6) ** 2
    } else {
        return Math.sin((time-0.6) * ((3 * Math.PI) / 0.4)) * 0.2 + 1
    }
}

我们改造一下之前的 animate 函数,接收一个 easing 方法。

/**
 *  执行补间动画方法
 *
 * @param      {Number}    start     开始数值
 * @param      {Number}    end       结束数值
 * @param      {Number}    time      补间时间
 * @param      {Function}  callback  每帧回调
 * @param      {Function}  easing    缓动方法,默认匀速
 */
function animate(start, end, time, callback, easing = t => t) {
    let startTime = performance.now() // 设置开始的时间戳
    let differ = end - start // 拿到数值差值
    // 创建每帧之前要执行的函数
    function loop() {
        raf = requestAnimationFrame(loop) // 下一阵调用每帧之前要执行的函数
        const passTime = performance.now() - startTime // 获取当前时间和开始时间差
        let per = passTime / time // 计算当前已过百分比
        if (per >= 1) { // 判读如果已经执行
            per = 1 // 设置为最后的状态
            cancelAnimationFrame(raf) // 停掉动画
        }
        const pass = differ * easing(per) // 通过已过时间百分比*开始结束数值差得出当前的数值
        callback(pass)
    }
    let raf = requestAnimationFrame(loop) // 下一阵调用每帧之前要执行的函数
}

测试一下,将我们刚刚创建的 easing 方法传进来

加速运动

let box = document.querySelector('.box')
animate(0, 500, 400, value => {
    box.style.transform = `translateX(${value}px)` // 将数值设置给 方块 的 css 属性 transform 属性可以控制元素在水平方向上的位移
}, easeIn)

加速后抖一抖

let box = document.querySelector('.box')
animate(0, 500, 400, value => {
    box.style.transform = `translateX(${value}px)` // 将数值设置给 方块 的 css 属性 transform 属性可以控制元素在水平方向上的位移
}, shake)

总结

这些只是 JavaScript 动画基础中的基础,理解动画的原理后,再做动画就更得心应手了。

市面上有很多JS动画库,大家可以开箱即用。有一些是针对DOM操作的,也有一些是针对 JavaScript 对象。实现原理你都已经懂了。

上述代码已发布到Github: https://github.com/fanmingfei/animation-base

我的另外两篇关于动画的文章:

版权声明

本文资源来源互联网,仅供学习研究使用,版权归该资源的合法拥有者所有,

本文仅用于学习、研究和交流目的。转载请注明出处、完整链接以及原作者。

原作者若认为本站侵犯了您的版权,请联系我们,我们会立即删除!

原文标题

大话 JavaScript 动画

原文链接

https://segmentfault.com/a/1190000016592210

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Baozi Training Leetcode solution 1176: Diet Plan Performance

    A dieter consumes calories[i] calories on the i-th day.

    包子面试培训
  • 前端框架三巨头:React仍是老大,Vue 增长率最高

    IT派 - {技术青年圈} 持续关注互联网、区块链、人工智能领域 前端框架的走势图是通过统计最新的数据,并与前一年的数据进行比较而生成。数据来源于 npm ...

    IT派
  • 【从零学习OpenCV 4】Mat类构造与赋值

    前一小节已经介绍了三种构造Mat类变量的方法,但是后两种没有给变量初始化赋值,本小节将重点介绍如何灵活的构造并赋值Mat类变量。根据OpenCV的源码定义,关于...

    小白学视觉
  • 线上环境 Linux 系统调用追踪

    提到如何动态追踪进程中的系统调用,相信大家第一时间都能想到 strace,它的基本用法非常简单,非常适合用来解决 “为什么这个软件无法在这台机器上运行?” 这类...

    PingCAP
  • 有坑勿踩(二): 关于游标

    聊一聊一个最基本的问题,游标的使用。可能你从来没有注意过它,但其实它在MongoDB的使用中是普遍存在的,也存在一些常见的坑需要引起我们的注意。

    MongoDB中文社区
  • 有坑勿踩(二): 关于游标

    聊一聊一个最基本的问题,游标的使用。可能你从来没有注意过它,但其实它在MongoDB的使用中是普遍存在的,也存在一些常见的坑需要引起我们的注意。

    MongoDB中文社区
  • 有点牛逼,滴滴开源的分布式id生成系统

    ID Generator id生成器 分布式id生成系统,简单易用、高性能、高可用的id生成系统

    用户6543014

扫码关注云+社区

领取腾讯云代金券