动画开发 --- css和javascript方式

Transition

CSS3 过渡是元素从一种样式逐渐改变为另外一种的效果git

描述
transition-property 指定CSS属性的name,transition效果
transition-duration transition效果须要指定多少秒或毫秒才能完成
transition-timing-function 指定transition效果的转速曲线
transition-delay 定义transition效果开始的时候

transition-property

描述
none 没有属性会得到过渡效果。
all 全部属性都将得到过渡效果。
property 定义应用过渡效果的 CSS 属性名称列表,列表以逗号分隔。

transition-timing-function

描述
linear 规定以相同速度开始至结束的过渡效果(等于 cubic-bezier(0,0,1,1))。
ease 规定慢速开始,而后变快,而后慢速结束的过渡效果(cubic-bezier(0.25,0.1,0.25,1))。
ease-in 规定以慢速开始的过渡效果(等于 cubic-bezier(0.42,0,1,1))。
ease-out 规定以慢速结束的过渡效果(等于 cubic-bezier(0,0,0.58,1))。
ease-in-out 规定以慢速开始和结束的过渡效果(等于 cubic-bezier(0.42,0,0.58,1))。
cubic-bezier(n,n,n,n) 在 cubic-bezier 函数中定义本身的值。可能的值是 0 至 1 之间的数值。

Animation

建立动画github

说明
animation-name 指定要绑定到选择器的关键帧的名称
animation-duration 动画指定须要多少秒或毫秒完成
animation-timing-function 设置动画将如何完成一个周期
animation-delay 设置动画在启动前的延迟间隔。
animation-iteration-count 定义动画的播放次数。infinite指定动画应该播放无限次(永远)
animation-direction 指定是否应该轮流反向播放动画。
animation-fill-mode 规定当动画不播放时(当动画完成时,或当动画有一个延迟未开始播放时),要应用到元素的样式。
animation-play-state 指定动画是否正在运行或已暂停。

animation-timing-function

描述
linear 动画从头至尾的速度是相同的。
ease 默认。动画以低速开始,而后加快,在结束前变慢。
ease-in 动画以低速开始。
ease-out 动画以低速结束。
ease-in-out 动画以低速开始和结束。
cubic-bezier(n,n,n,n) 在 cubic-bezier 函数中本身的值。可能的值是从 0 到 1 的数值。

animation-direction

描述
normal 默认值。动画按正常播放。
reverse 动画反向播放。
alternate 动画在奇数次(一、三、5...)正向播放,在偶数次(二、四、6...)反向播放。
alternate-reverse 动画在奇数次(一、三、5...)反向播放,在偶数次(二、四、6...)正向播放。

animation-fill-mode

描述
none 默认值。动画在动画执行以前和以后不会应用任何样式到目标元素。
forwards 在动画结束后(由 animation-iteration-count 决定),动画将应用该属性值。
backwards 动画将应用在 animation-delay 定义期间启动动画的第一次迭代的关键帧中定义的属性值。这些都是 from 关键帧中的值(当 animation-direction 为 "normal" 或 "alternate" 时)或 to 关键帧中的值(当 animation-direction 为 "reverse" 或 "alternate-reverse" 时)。
both 动画遵循 forwards 和 backwards 的规则。也就是说,动画会在两个方向上扩展动画属性。

animation-play-state

描述
paused 指定暂停动画
running 指定正在运行的动画

浏览器事件循环机制

浏览器界面的背后隐藏着不少用户感知不到的事件执行机制,包括加载资源,渲染,网络请求,用户交互等多种行为,动画也是其中的一部分,由于咱们没法控制行为的优先级顺序,了解这些机制对开发怎么更好使用动画有好处.web

  1. 全部的任务都被放主线程上运行造成一个执行栈
  2. 同步任务直接执行并阻塞后续任务等待结束,其中遇到一些异步任务会新开线程去执行该任务而后往下执行,异步任务执行完返回结果以后就把回调事件加入到任务队列(Queue)
  3. 执行栈(execution context stack)全部任务执行完以后,会到任务队列(Queue)里提取全部的微任务队列(micro tasks)事件执行完, 若是在微任务的执行中又加入了新的微任务,也会在这一步一块儿执行;
  4. 一次循环结束,GUI渲染线程接管检查,检测是否有渲染机会( rendering opportunity),渲染机会根据物理环境决定(依赖机子性能,根据屏幕刷新率、页面性能、页面是否在后台运行来共同决定);算法

    • 浏览器会尽量的保持帧率稳定,例如页面性能没法维持 60fps(每 16.66ms 渲染一次)的话,那么浏览器就会选择 30fps 的更新速率,而不是偶尔丢帧。
    • 若是浏览器上下文不可见,那么页面会下降到 4fps 左右甚至更低。
    • 即便知足上面条件依然可能跳过渲染

      a. 浏览器判断更新渲染不会带来视觉上的改变。promise

      b. 帧动画回调为空浏览器

  5. 若是本轮不渲染则推迟也不会执行下面的操做,多个任务会合并到下一轮可渲染的时候,也有利于优化它们之间状态的合并或者重合,互相抵消等而出现的渲染消耗
  6. 对于须要渲染的文档,网络

    • 若是窗口的大小发生了变化,执行监听的 resize 方法。
    • 若是页面发生了滚动,执行 scroll 方法。
    • 执行帧动画回调,也就是 requestAnimationFrame 的回调。
    • 执行 IntersectionObserver 的回调。
  7. 从新渲染绘制用户界面。
  8. 判断异步队列是否都为空,若是是的话,则进行 Idle 闲置周期的算法,判断是否要执行 requestIdleCallback的回调函数。

浏览器会在保持任务顺序的前提下,可能分配四分之三的优先权给用户交互行为(鼠标和键盘事件),保证用户的输入获得最高优先级的响应,而剩下的优先级交给其余 Task,而且保证不会“饿死”它们。框架

对于resizescroll来讲,并非到了这一步才去执行滚动和缩放,浏览器会保存一个 pending scroll event targets,等到事件循环中的 scroll这一步,去派发一个事件到对应的目标上,驱动它去执行监听的回调函数而已。resize也是同理。异步

requestAnimationFrame

/**
 * 你但愿执行一个动画,而且要求浏览器在下次重绘以前调用指定的回调函数更新动画
 * @param callback
 * @return 请求 ID
 */
window.requestAnimationFrame(callback)
描述
callback 下一次重绘以前更新动画帧所调用的函数。该回调函数会被传入DOMHighResTimeStamp参数,该参数与performance.now()的返回值相同,它表示requestAnimationFrame()开始去执行回调函数的时刻。
返回值 一个 long 整数,请求 ID ,是回调列表中惟一的标识。是个非零值,没别的意义。你能够传这个值给 window.cancelAnimationFrame() 以取消回调函数。

回调函数执行次数一般是每秒60次,但在大多数遵循W3C建议的浏览器中,回调函数执行次数一般与浏览器屏幕刷新次数相匹配。为了提升性能和电池寿命,所以在大多数浏览器里,当requestAnimationFrame() 运行在后台标签页或者隐藏的iframe里时,requestAnimationFrame() 会被暂停调用以提高性能和电池寿命。函数

回调函数会被传入DOMHighResTimeStamp参数,DOMHighResTimeStamp指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间。在同一个帧中的多个回调函数,它们每个都会接受到一个相同的时间戳,即便在计算上一个回调函数的工做负载期间已经消耗了一些时间。该时间戳是一个十进制数,单位毫秒,最小精度为1ms(1000μs)。

DOMHighResTimeStamp 是一个double类型,用于存储时间值。该值能够是离散的时间点或两个离散时间点之间的时间差。T单位为毫秒 ms (milliseconds) ,应准确至5微秒 µs (microseconds)。可是,若是浏览器没法提供准确到5微秒的时间值(例如,因为硬件或软件的限制), 浏览器能够以毫秒为单位的精确到毫秒的时间表示该值。

requestAnimationFrame VS 定时器

咱们分别用两个API实现同一个动画效果,看看他们表现怎么样

setTimeout

const gap = 16.6
let num = 0
let timer

function run() {
    timer = setTimeout(() => {
        num++
        document.body.style.background = num % 2 ? "red" : "blue"
        console.log(num)
        num < 30 && run()
    }, gap)
}
run()

image

requestAnimationFrame

let num = 0

function run() {
    window.requestAnimationFrame(() => {
        num++
        document.body.style.background = num % 2 ? "red" : "blue"
        console.log(num)
        num < 30 && run()
    })
}
run()

image

结论

setTimeout存在不绘制的状况,视觉上看就是掉帧的情形,而requestAnimationFrame 会规律绘制界面,缘由在于通常状况下, 浏览器的帧率跟屏幕帧率一致, 基本都是60, 也就是16ms左右会刷新一次.定时器只是计时完毕把对应任务添加处处理队列,依然要等执行栈空闲才会去提取队列执行,这些形成的结果就是

  • 若是定时器时间小于帧率,在下一次渲染以前已经执行过屡次定时器回调
  • 定时器回调会被主线程其余任务阻塞执行,每次时间偏差会影响后续执行时间

这两个影响形成用定时器作动画会常常传画面掉帧或者速率不正常的情形.

requestAnimationFrame是在浏览器在下次重绘以前调用,即它的调用时机由系统本身把握,能够保证在闲置时间内执行,还有一个优势是当页面切换其余应用或者隐藏以后会暂停调用以提高性能和电池寿命。

还有另外一个定时器setInterval也有一些问题

  • 累计效应,若是执行栈阻塞时间足够长以致于队列中已经存在多个setInterval的对应任务的状况,执行时间会远低于开发者指望的结果;
  • 部分浏览器(如Safari等)滚动过程当中执行JS,容易形成卡顿和未知错误;
  • 浏览器最小化显示时setInterval会继续执行,可是对应任务会等到浏览器还原再一瞬间所有执行;

requestIdleCallback(实验中)

此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以获得在不一样浏览器中适合使用的前缀。因为该功能对应的标准文档可能被从新修订,因此在将来版本的浏览器中该功能的语法和行为可能随之改变。
/**
 * 将在浏览器的闲置时段内调用的函数排队
 * @param callback
 * @param options 
 */
window.requestIdleCallback(callback[, options])
描述
callback 一个在事件循环空闲时即将被调用的函数的引用。函数会接收到一个名为 IdleDeadline的参数,这个参数能够获取当前空闲时间以及回调是否在超时时间前已经执行的状态。
options(可选) timeout:若是指定了timeout并具备一个正值,而且还没有经过超时毫秒数调用回调,那么回调会在下一次空闲时期被强制执行,尽管这样极可能会对性能形成负面影响。

React实现了一套相似的时间分片渲染机制

摘要

web开发使用这个API可以协同调度后台任务,这样它们就不会给共享相同循环阶段的其余高优先级任务,例如输入处理,动画和帧合成带来延迟.用户代理可以基于它对当前调度任务的了解,垂直同步(vsync?)延迟,用户交互等等更好肯定何时适合运行后台任务,而不会在动画和输入响应引发用户可察觉的延迟或闪烁(jank?).所以,当浏览器将处于闲置的时候使用这个API可以更适当的调度后台任务.

闲置周期

在给定帧内完成输入处理,渲染和合成以后.用户代理的主线程一般处于闲置直到下一帧开始,另外一个等待任务会有资格参与运行.或者接收到用户输入,这规范提供一种方式在其余闲置时间经由requestIdleCallback API去调度执行回调

经过requestIdleCallback API回调的方式能够在用户代理定义的闲置周期执行,它会给予一个符合的截止日期对应当前闲置周期的结束时间.这决定用户代理怎么定义闲置周期的构成,可是预期它们会发生在静止期间,浏览器处于闲置周期.

一个闲置周期的例子是在动态动画处于将给定帧提交到屏幕和开始处理下一帧之间的时间里

Figure 1 Example of an inter-frame idle period

image

在动态动画和屏幕更新期间,这样的闲置时间会频繁出现,但一般都很短(即,小于16ms的设备与60Hz的垂直同步周期)

注意

web开发应该仔细考虑在闲置回调期间内全部由操做完成的工做.一些操做例如promise解析或触发页面布局,可能致使随后的任务会在闲置周期完成后才调度,在这种状况下,应用程序应该在截止日期到期以前完成这些额外的工做,从而容许在下一个框架截止日期以前执行这些操做。

Figure 2 Example of an idle period when there are no pending frame updates

image

另外一个例子是用户代理在闲置周期内但没有屏幕更新发生,在这种状况下,用户代理可能没有能够绑定闲置期结束的后续任务,以免在不可预测的任务中形成用户可察觉的延迟例如用户输入处理,这些闲置时间的长度应该被限制到50ms的最大值,一旦一个闲置周期已经完成后用户代理能够立马调度另外一个闲置周期(若是他保持闲置),如图2所示,以使后台工做在更长闲置时间内继续发生。

在闲置周期内用户代理会以先进先出的顺序运行回调直到闲置周期结束或者没有更多的闲置回调有资格运行.就其自己而论,用户代理并不须要在一个闲置周期内运行完当前发布的全部闲置回调.任何剩余的闲置任务均可以在下一个闲置期间运行。

注意

为开发者们提供最佳性能,鼓励消除没必要要的回调(例如requestAnimationFrame, setTimeout等等),他们并不执行有意义的工做,不要保持这些回调触发并等待事件的响应.取而代之的是根据须要去调度它们以便在它们可用时对事件做出反应,这种模式提升了总体效率而且容许用户代理去调度长闲置周期(直到50ms)可用于高效地执行大块的后台工做。

由于还没标准化,因此更新细节能够查看 这里

相关文章
相关标签/搜索