从2017年春节是几月几号3月8号到10月16号为止总共多少天

966,690 一月 独立访问用户
语言 & 开发
架构 & 设计
文化 & 方法
您目前处于:
Javascript高性能动画与页面渲染
Javascript高性能动画与页面渲染
日. 估计阅读时间:
注意: 挥一挥衣袖,带走满满干货,关注,时不时发福利呦!
相关厂商内容
相关赞助商
QCon北京-18日,北京&国家会议中心,
接下来的工作中,我们将会用到这些工具,来实时查看我们页面的性能。
60fps是动力也是压力,因为它意味着我们只有16.7毫秒(1000 / 60)来绘制每一帧。如果使用setTimeout或者setInterval(以下统称为timer)来控制绘制,问题就来了。
首先,Timer计算延时的精确度不够。延时的计算依靠的是浏览器的内置时钟,而时钟的精确度又取决于时钟更新的频率(Timer resolution)。IE8及其之前的IE版本更新间隔为15.6毫秒。假设你设定的setTimeout延迟为16.7ms,那么它要更新两个15.6毫秒才会该触发延时。这也意味着无故延迟了 15.6 x 2 - 16.7 = 14.5毫秒。
DELAY: |------------|
CLOCK: |----------|----------|
所以即使你给setTimeout设定的延时为0ms,它也不会立即触发。目前Chrome与IE9+浏览器的更新频率都为4ms(如果你使用的是笔记本电脑,并且在使用电池而非电源的模式下,为了节省资源,浏览器会将更新频率切换至于系统时间相同,也就意味着更新频率更低)。
退一步说,假使timer resolution能够达到16.7ms,它还要面临一个异步队列的问题。因为异步的关系setTimeout中的回调函数并非立即执行,而是需要加入等待队列中。但问题是,如果在等待延迟触发的过程中,有新的同步脚本需要执行,那么同步脚本不会排在timer的回调之后,而是立即执行,比如下面这段代码:
function runForSeconds(s) {
var start = +new Date();
while (start + s * 1000 & (+new Date())) {}
document.body.addEventListener(&click&, function () {
runForSeconds(10);
}, false);
setTimeout(function () {
console.log(&Done!&);
}, 1000 * 3);
如果在等待触发延迟的3秒过程中,有人点击了body,那么回调还是准时在3s完成时触发吗?当然不能,它会等待10s,同步函数总是优先于异步函数:
等待3秒延迟 |
|---&console.log(&Done!&);
|----1s----|----2s----|
|---&console.log(&Done!&);
点击body后
以为是这样:|----1s----|----2s----|----3s----|---&console.log(&Done!&)---&|------------------10s----------------|
其实是这样:|----1s----|----2s----|------------------10s----------------|---&console.log(&Done!&);
John Resign有三篇关于Timer性能与准确性的文章: 1., 2., 3.。从文章中可以看到Timer在不同平台浏览器与操作系统下的一些问题。
再退一步说,假设timer resolution能够达到16.7ms,并且假设异步函数不会被延后,使用timer控制的动画还是有不尽如人意的地方。这也就是下一节要说的问题。
垂直同步问题
这里请再允许我引入另一个常量60&&屏幕的刷新率60Hz。
60Hz和60fps有什么关系?没有任何关系。fps代表GPU渲染画面的频率,Hz代表显示器刷新屏幕的频率。一幅静态图片,你可以说这副图片的fps是0帧/秒,但绝对不能说此时屏幕的刷新率是0Hz,也就是说刷新率不随图像内容的变化而变化。游戏也好浏览器也好,我们谈到掉帧,是指GPU渲染画面频率降低。比如跌落到30fps甚至20fps,但因为视觉暂留原理,我们看到的画面仍然是运动和连贯的。
接上一节,我们假设每一次timer都不会有延时,也不会被同步函数干扰,甚至能把时间缩短至16ms,那么会发生什么呢:
(点击图像放大)
在22秒处发生了丢帧
如果把延迟时间缩的更短,丢失的帧数也就更多:
实际情况会比以上想象的复杂的多。即使你能给出一个固定的延时,解决60Hz屏幕下丢帧问题,那么其他刷新频率的显示器应该怎么办,要知道不同设备、甚至相同设备在不同电池状态下的屏幕刷新率都不尽相同。
以上同时还忽略了屏幕刷新画面的时间成本。问题产生于GPU渲染画面的频率和屏幕刷新频率的不一致:如果GPU渲染出一帧画面的时间比显示器刷新一张画面的时间要短(更快),那么当显示器还没有刷新完一张图片时,GPU渲染出的另一张图片已经送达并覆盖了前一张,导致屏幕上画面的撕裂,也就是是上半部分是前一张图片,下半部分是后一张图片:
PC游戏中解决这个问题的方法是开启垂直同步(v-sync),也就是让GPU妥协,GPU渲染图片必须在屏幕两次刷新之间,且必须等待屏幕发出的垂直同步信号。但这样同样也是要付出代价的:降低了GPU的输出频率,也就降低了画面的帧数。以至于你在玩需要高帧数运行的游戏时(比如竞速、第一人称射击)感觉到&顿卡&,因为掉帧。
requestAnimationFrame
在这里不谈requestAnimationFrame(以下简称rAF)用法,具体请参考。我们来具体谈谈rAF所解决的问题。
从上一节我们可以总结出实现平滑动画的两个因素
时机(Frame Timing): 新的一帧准备好的时机
成本(Frame Budget): 渲染新的一帧需要多长的时间
这个Native API把我们从纠结于多久刷新的一次的困境中解救出来(其实rAF也不关心距离下次屏幕刷新页面还需要多久)。当我们调用这个函数的时候,我们告诉它需要做两件事: 1. 我们需要新的一帧;2.当你渲染新的一帧时需要执行我传给你的回调函数
那么它解决了我们上面描述的第一个问题,产生新的一帧的时机。
那么第二个问题呢。不,它无能为力。比如可以对比下面两个页面:
对比两个页面的源码,你会发现只有一处不同:
// animation loop
function update(timestamp) {
for(var m = 0; m & movers. m++) {
// DEMO 版本
//movers[m].style.left = ((Math.sin(movers[m].offsetTop + timestamp/1000)+1) * 500) + 'px';
// FIXED 版本
movers[m].style.left = ((Math.sin(m + timestamp/1000)+1) * 500) + 'px';
rAF(update);
rAF(update);
DEMO版本之所以慢的原因是,在修改每一个物体的left值时,会请求这个物体的offsetTop值。这是一个非常耗时的reflow操作(具体还有哪些耗时的reflow操作可以参考这篇: )。这一点从Chrome调试工具中可以看出来(截图中的某些功能需要在Chrome canary版本中才可启用)
未矫正的版本
可见大部分时间都花在了rendering上,而矫正之后的版本:
rendering时间大大减少了
但如果你的回调函数耗时真的很严重,rAF还是可以为你做一些什么的。比如当它发现无法维持60fps的频率时,它会把频率降低到30fps,至少能够保持帧数的稳定,保持动画的连贯。
使用rAF推迟代码
没有什么是万能的,面对上面的情况,我们需要对代码进行组织和优化。
看看下面这样一段代码:
function jank(second) {
var start = +new Date();
while (start + second * 1000 & (+new Date())) {}
div.style.backgroundColor = &red&;
// some long run task
div.style.backgroundColor = &blue&;
无论在任何的浏览器中运行上面的代码,你都不会看到div变为红色,页面通常会在假死5秒,然后容器变为蓝色。这是因为浏览器的始终只有一个线程在运行(可以这么理解,因为js引擎与UI引擎互斥)。虽然你告诉浏览器此时div背景颜色应该为红色,但是它此时还在执行脚本,无法调用UI线程。
有了这个前提,我们接下来看这段代码:
var div = document.getElementById(&foo&);
var currentWidth = div.innerW
div.style.backgroundColor = &blue&;
// do some &long running& task, like sorting data
这个时候我们不仅仅需要更新背景颜色,还需要获取容器的宽度。可以想象它的执行顺序如下:
当我们请求innerWidth一类的属性时,浏览器会以为我们马上需要,于是它会立即更新容器的样式(通常浏览器会攒着一批,等待时机一次性的repaint,以便节省性能),并把计算的结果告诉我们。这通常是性能消耗量大的工作。
但如果我们并非立即需要得到结果呢?
上面的代码有两处不足,
更新背景颜色的代码过于提前,根据前一个例子,我们知道,即使在这里告知了浏览器我需要更新背景颜色,浏览器至少也要等到js运行完毕才能调用UI线程;
假设后面部分的long runing代码会启动一些异步代码,比如setTimeout或者Ajax请求又或者web-worker,那应该尽早为妙。
综上所述,如果我们不是那么迫切的需要知道innerWidth,我们可以使用rAF推迟这部分代码的发生:
requestAnimationFrame(function(){
var el = document.getElementById(&foo&);
var currentWidth = el.innerW
el.style.backgroundColor = &blue&;
// do some &long running& task, like sorting data
可见即使我们在这里没有使用到动画,但仍然可以使用rAF优化我们的代码。执行的顺序会变成:
在这里rAF的用法变成了:把代码推迟到下一帧执行。
有时候我们需要把代码推迟的更远,比如这个样子:
再比如我们想要一个效果分两步执行:1.div的display变为block;2. div的top值缩短移动到某处。如果这两项操作都放入同一帧中的话,浏览器会同时把这两项更改应用于容器,在同一帧内。于是我们需要两帧把这两项操作区分开来:
requestAnimationFrame(function(){
el.style.display = &block&;
requestAnimationFrame(function(){
// fire off a CSS transition on its `top` property
el.style.top = &300px&;
这样的写法好像有些不太讲究,Kyle Simpson有一个开源项目,它把上面的用法封装了起来,并且提供了API。实现起来非常简单,摘一段代码瞧瞧:
function qID(){
id = Math.floor(Math.random() * 1E9);
} while (id in q_ids);
function queue(cb) {
var qid = qID();
q_ids[qid] = rAF(function(){
delete q_ids[qid];
cb.apply(publicAPI,arguments);
function queueAfter(cb) {
qid = queue(function(){
// do our own rAF call here because we want to re-use the same `qid` for both frames
q_ids[qid] = rAF(function(){
delete q_ids[qid];
cb.apply(publicAPI,arguments);
使用方法:
// 插入下一帧
id1 = aFrame.queue(function(){
text = document.createTextNode(&##&);
body.appendChild(text);
// 插入下下一帧
id2 = aFrame.queueAfter(function(){
text = document.createTextNode(&!!&);
body.appendChild(text);
使用rAF解耦代码
先从一个2011年twitter遇到的bug说起。
当时twitter加入了一个新功能:&无限滚动&。也就是当页面滚至底部的时候,去加载更多的twitter:
$(window).bind('scroll', function () {
if (nearBottomOfPage()) {
// load more tweets ...
但是在这个功能上线之后,发现了一个严重的bug:经过几次滚动到最底部之后,滚动就会变得奇慢无比。
经过排查发现,原来是一条语句引起的:$details.find(&.details-pane-outer&);
这还不是真正的罪魁祸首,真正的原因是因为他们将使用的jQuery类库从1.4.2升级到了1.4.4版。而这jQuery其中一个重要的升级是把Sizzle的上下文选择器全部替换为了querySelectorAll。但是这个接口原实现使用的是getElementsByClassName。虽然querySelectorAll在大部分情况下性能还是不错的。但在通过Class名称选择元素这一项是占了下风。有两个对比测试可以看出来:1.
通过这个bug,John Resig给出了一条(实际上是两条,但是今天只取与我们话题有关的)非常重要的建议
It&s a very, very, bad idea to attach handlers to the window scroll event.
他想表达的意思是,像scroll,resize这一类的事件会非常频繁的触发,如果把太多的代码放进这一类的回调函数中,会延迟页面的滚动,甚至造成无法响应。所以应该把这一类代码分离出来,放在一个timer中,有间隔的去检查是否滚动,再做适当的处理。比如如下代码:
var didScroll =
$(window).scroll(function() {
didScroll =
setInterval(function() {
if ( didScroll ) {
didScroll =
// Check your page position and then
// Load in more results
这样的作法类似于Nicholas将需要长时间运算的循环分解为&片&来进行运算:
// 具体可以参考他写的《javascript高级程序设计》
// 也可以参考他的这篇博客: http://www.nczonline.net/blog//speed-up-your-javascript-part-1/
function chunk(array, process, context){
var items = array.concat();
//clone the array
setTimeout(function(){
var item = items.shift();
process.call(context, item);
if (items.length & 0){
setTimeout(arguments.callee, 100);
原理其实是一样的,为了优化性能、为了防止浏览器假死,将需要长时间运行的代码分解为小段执行,能够使浏览器有时间响应其他的请求。
回到rAF上来,其实rAF也可以完成相同的功能。比如最初的滚动代码是这样:
function onScroll() {
function update() {
// assume domElements has been declared
for(var i = 0; i & domElements. i++) {
// read offset of DOM elements
// to determine visibility - a reflow
// then apply some CSS classes
// to the visible items - a repaint
window.addEventListener('scroll', onScroll, false);
这是很典型的反例:每一次滚动都需要遍历所有元素,而且每一次遍历都会引起reflow和repaint。接下来我们要做的事情就是把这些费时的代码从update中解耦出来。
首先我们仍然需要给scroll事件添加回调函数,用于记录滚动的情况,以方便其他函数的查询:
var latestKnownScrollY = 0;
function onScroll() {
latestKnownScrollY = window.scrollY;
接下来把分离出来的repaint或者reflow操作全部放入一个update函数中,并且使用rAF进行调用:
function update() {
requestAnimationFrame(update);
var currentScrollY = latestKnownScrollY;
// read offset of DOM elements
// and compare to the currentScrollY value
// then apply some CSS classes
// to the visible items
// kick off
requestAnimationFrame(update);
其实解耦的目的已经达到了,但还需要做一些优化,比如不能让update无限执行下去,需要设标志位来控制它的执行:
var latestKnownScrollY = 0,
function onScroll() {
latestKnownScrollY = window.scrollY;
requestTick();
function requestTick() {
if(!ticking) {
requestAnimationFrame(update);
并且我们始终只需要一个rAF实例的存在,也不允许无限次的update下去,于是我们还需要一个出口:
function update() {
// reset the tick so we can
// capture the next onScroll
var currentScrollY = latestKnownScrollY;
// read offset of DOM elements
// and compare to the currentScrollY value
// then apply some CSS classes
// to the visible items
// kick off - no longer needed! Woo.
// update();
Kyle Simpson说:
Rule of thumb: don&t do in JS what you can do in CSS.
如以上所说,即使使用rAF,还是会有诸多的不便。我们还有一个选择是使用css动画:虽然浏览器中UI线程与js线程是互斥,但这一点对css动画不成立。
在这里不聊css动画的用法。css动画运用的是什么原理来提升浏览器性能的。
首先我们看看淘宝首页的焦点图:
我想提出一个问题,为什么明明可以使用translate 2d去实现的动画,它要用3d去实现呢?
我不是淘宝的员工,但我的第一猜测这么做的原因是为了使用translate3d hack。简单来说如果你给一个元素添加上了-webkit-transform: translateZ(0);或者-webkit-transform: translate3d(0,0,0);属性,那么你就等于告诉了浏览器用GPU来渲染该层,与一般的CPU渲染相比,提升了速度和性能。(我很确定这么做会在Chrome中启用了硬件加速,但在其他平台不做保证。就我得到的资料而言,在大多数浏览器比如Firefox、Safari也是适用的)。
但这样的说法其实并不准确,至少在现在的Chrome版本中这算不上一个hack。因为默认渲染所有的网页时都会经过GPU。那么这么做还有必要吗?有。在理解原理之前,你必须先了解一个层(Layer)的概念。
html在浏览器中会被转化为DOM树,DOM树的每一个节点都会转化为RenderObject, 多个RenderObject可能又会对应一个或多个RenderLayer。浏览器渲染的流程如下:
获取 DOM 并将其分割为多个层(RenderLayer)
将每个层栅格化,并独立的绘制进位图中
将这些位图作为纹理上传至 GPU
复合多个层来生成最终的屏幕图像(终极layer)。
这和游戏中的3D渲染类似,虽然我们看到的是一个立体的人物,但这个人物的皮肤是由不同的图片&贴&和&拼&上去的。网页比此还多了一个步骤,虽然最终的网页是由多个位图层合成的,但我们看到的只是一个复印版,最终只有一个层。当然有的层是无法拼合的,比如flash。以爱奇艺的一个播放页(
我们可以看到页面上由如下层组成:
OK,那么问题来了。
假设我现在想改变一个容器的样式(可以看做动画的一个步骤),并且是一种最糟糕的情况,改变它的长和宽&&为什么说改变长和宽是最糟糕的情况呢。通常改变一个物体的样式需要以下四个步骤:
任何属性的改变都导致浏览器重新计算容器的样式,比如你改变的是容器的尺寸或者位置(reflow),那么首先影响的就是容器的尺寸和位置(也影响了与它相关的父节点自己点相邻节点的位置等),接下来浏览器还需要对容器重新绘制(repaint);但如果你改变的只是容器的背景颜色等无关容器尺寸的属性,那么便省去了第一步计算位置的时间。也就是说如果改变属性在瀑布图中开始的越早(越往上),那么影响就越大,效率就越低。reflow和repaint会导致所有受影响节点所在layer的位图重绘,反复执行上面的过程,导致效率降低。
为了把代价降到最低,当然最好只留下compositing layer这一个步骤即可。假设当我们改变一个容器的样式时,影响的只是它自己,并且还无需重绘,直接通过在GPU中改变纹理的属性来改变样式,岂不是更好?这当然是可以实现的,前提是你有自己的layer
这也是上面硬件加速hack的原理,也是css动画的原理&&给元素创建自己layer,而非与页面上大部分的元素共用layer。
什么样的元素才能创建自己layer呢?在Chrome中至少要符合以下条件之一:
Layer has 3D or perspective transform CSS properties(有3D元素的属性)
Layer is used by &video& element using accelerated video decoding(video标签并使用加速视频解码)
Layer is used by a &canvas& element with a 3D context or accelerated 2D context(canvas元素并启用3D)
Layer is used for a composited plugin(插件,比如flash)
Layer uses a CSS animation for its opacity or uses an animated webkit transform(CSS动画)
Layer uses accelerated CSS filters(CSS滤镜)
Layer with a composited descendant has information that needs to be in the composited layer tree, such as a clip or reflection(有一个后代元素是独立的layer)
Layer has a sibling with a lower z-index which has a compositing layer (in other words the layer is rendered on top of a composited layer)(元素的相邻元素是独立layer)
很明显刚刚我们看到的播放页中的flash和开启了translate3d样式的焦点图符合上面的条件。
同时你也可以勾选Chrome开发工具中的rendering选显卡下的Show composited layer borders 选项。页面上的layer便会加以边框区别开来。为了验证我们的想法,看下面这样一段代码:
&style type=&text/css&&
-webkit-animation-duration: 5s;
-webkit-animation-name:
-webkit-animation-iteration-count:
-webkit-animation-direction:
width: 200
height: 200
margin: 100
background-color:
@-webkit-keyframes slide {
-webkit-transform: rotate(0deg);
-webkit-transform: rotate(120deg);
&div id=&foo&&I am a strange root.&/div&
运行时的timeline截图如下:
可见元素有自己的layer,并且在动画的过程中没有触发reflow和repaint。
最后再看看淘宝首页,不仅仅只有焦点图才拥有了独立的layer:
但太多的layer也未必是一件好事情,有兴趣的同学可以看一看这篇文章:。看一看在苹果首页太多layer时出现的问题。
参考文章:
感谢对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博()或者腾讯微博()关注我们,并与我们的编辑和其他读者朋友交流。
Author Contacted
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
深入浅出,很好懂
Wong Peter
dehui fang
垂直同步问题 段的丢帧看不太明白
liu xingbo
很少看到文章有这么多人说好的
jiang changren
Shen Michael
Cheung Arale
jianhui xue
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
赞助商链接
InfoQ每周精要
通过个性化定制的新闻邮件、RSS Feeds和InfoQ业界邮件通知,保持您对感兴趣的社区内容的时刻关注。
架构 & 设计
文化 & 方法
<及所有内容,版权所有 &#169;
C4Media Inc.
服务器由 提供, 我们最信赖的ISP伙伴。
北京创新网媒广告有限公司
京ICP备号-7
注意:如果要修改您的邮箱,我们将会发送确认邮件到您原来的邮箱。
使用现有的公司名称
修改公司名称为:
公司性质:
使用现有的公司性质
修改公司性质为:
使用现有的公司规模
修改公司规模为:
使用现在的国家
使用现在的省份
Subscribe to our newsletter?
Subscribe to our industry email notices?
我们发现您在使用ad blocker。
我们理解您使用ad blocker的初衷,但为了保证InfoQ能够继续以免费方式为您服务,我们需要您的支持。InfoQ绝不会在未经您许可的情况下将您的数据提供给第三方。我们仅将其用于向读者发送相关广告内容。请您将InfoQ添加至白名单,感谢您的理解与支持。N卡专用GPU物理渲染器octane render渲染动画测试_显卡吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:2,279,192贴子:
N卡专用GPU物理渲染器octane render渲染动画测试收藏
请切换到超清模式观看!!!视频来自:
京东电脑节,全民抢宝进行时!1999抢i7本,半价秒电脑,抢直降3000显示器
N卡还玩这个
原视频下载
做3d动画的不是很爽?想看艾薇,自己渲染一个   --电脑修的好,备胎当到老;电脑修不好,备胎当不了
拿两个奶子测试物理效果?
你是在哪里下载到这个软件的?我下了一个破解版但是显示no cuda,我显卡是有cuda的。我怀疑是我软件有问题
表示接触过这个渲染器,GPU渲染器还有furryball,Vray也开发有显卡渲染器
那两个咪咪是不是物理引擎测试
摇得太假 像果冻一样
回收显卡就上百姓网,专业可靠,提供各类公司/个人物品回收服务,价格透明!百姓网,各种物品回收信息,覆盖全,千万公司,免费发布信息,轻松解决物品回收难题!
请问软件是在哪里下载的?求个地址。
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或【交流】GPU加速对渲染有什么作用吗_ae吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:395,561贴子:
【交流】GPU加速对渲染有什么作用吗收藏
我的是quadro,以为会很流弊才买的,结果不支持,强行改结果三维光线都没了,,,,,所以我想问问gpu加速到底有多大用处,,,,
系统全面的AE教程,从0基础到高级,大量实战案例教程,学AE首选教程,快速打造AE顶尖高手!AE名师教学,课程系统权威,注重实战,循序渐进,适合所有学员,学AE就上勤学网
顶。。。。。。。
来个人呀。。。。。。
非常不错的 亲
你可以试试gpu渲染器,比如oc
GPU加速是所有选项当中最优质的吧 可以实现实时渲染!如果不支持的话 可以更新一下驱动程序看下!
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或

我要回帖

更多关于 2017年春节是几月几号 的文章

 

随机推荐