从理论上讲,这些都是好主意,直到深入为止。 问题是您不能在不同步RAF的情况下对其进行节流,这会破坏现有RAF的用途。 所以,你让它全速运行,并在单独的循环更新数据,甚至一个单独的线程!
是的,我说了。您可以在浏览器中执行多线程JavaScript!
我知道有两种方法可以很好地解决这种问题,即使用更少的果汁并产生更少的热量。最终结果是准确的人机定时和机器效率。
抱歉,这有点罗word,但是这里...
方法1:通过setInterval更新数据,并通过RAF更新图形。
使用单独的setInterval来更新平移和旋转值,物理特性,碰撞等。将这些值保存在每个动画元素的对象中。将转换字符串分配给每个setInterval'frame'对象中的变量。将这些对象保留在数组中。将间隔设置为所需的fps(毫秒):ms =(1000 / fps)。这样可以保持稳定的时钟,无论RAF速度如何,在任何设备上都允许相同的fps。 不要在此处将变换分配给元素!
在requestAnimationFrame循环中,使用老式的for循环遍历数组-不要在这里使用较新的形式,它们很慢!
for(var i=0; i<sprite.length-1; i++){ rafUpdate(sprite[i]); }
在rafUpdate函数中,从数组中的js对象及其元素ID获取转换字符串。您应该已经将“ sprite”元素附加到变量上,或者可以通过其他方式轻松访问,因此您不会浪费时间在RAF中“获取”它们。将它们保留在以其html id命名的对象中的效果很好。在将其放入SI或RAF之前将其设置好。
使用RAF 仅更新您的变换,仅使用3D变换(甚至用于2d),并将CSS设置为“ will-change:transform;”。会改变的元素。这样可以使您的转换尽可能地与本机刷新率同步,插入GPU,并告诉浏览器最集中的位置。
所以你应该有这样的伪代码...
// refs to elements to be transformed, kept in an array
var element = [
mario: document.getElementById('mario'),
luigi: document.getElementById('luigi')
//...etc.
]
var sprite = [ // read/write this with SI. read-only from RAF
mario: { id: mario ....physics data, id, and updated transform string (from SI) here },
luigi: { id: luigi .....same }
//...and so forth
] // also kept in an array (for efficient iteration)
//update one sprite js object
//data manipulation, CPU tasks for each sprite object
//(physics, collisions, and transform-string updates here.)
//pass the object (by reference).
var SIupdate = function(object){
// get pos/rot and update with movement
object.pos.x += object.mov.pos.x; // example, motion along x axis
// and so on for y and z movement
// and xyz rotational motion, scripted scaling etc
// build transform string ie
object.transform =
'translate3d('+
object.pos.x+','+
object.pos.y+','+
object.pos.z+
') '+
// assign rotations, order depends on purpose and set-up.
'rotationZ('+object.rot.z+') '+
'rotationY('+object.rot.y+') '+
'rotationX('+object.rot.x+') '+
'scale3d('.... if desired
; //...etc. include
}
var fps = 30; //desired controlled frame-rate
// CPU TASKS - SI psuedo-frame data manipulation
setInterval(function(){
// update each objects data
for(var i=0; i<sprite.length-1; i++){ SIupdate(sprite[i]); }
},1000/fps); // note ms = 1000/fps
// GPU TASKS - RAF callback, real frame graphics updates only
var rAf = function(){
// update each objects graphics
for(var i=0; i<sprite.length-1; i++){ rAF.update(sprite[i]) }
window.requestAnimationFrame(rAF); // loop
}
// assign new transform to sprite's element, only if it's transform has changed.
rAF.update = function(object){
if(object.old_transform !== object.transform){
element[object.id].style.transform = transform;
object.old_transform = object.transform;
}
}
window.requestAnimationFrame(rAF); // begin RAF
这样可以使您对数据对象的更新和转换字符串同步到SI中所需的“帧”速率,并且将RAF中的实际转换分配同步到GPU刷新速率。因此,实际的图形更新仅在RAF中进行,而对数据的更改以及构建转换字符串在SI中进行,因此没有麻烦,而是“时间”以所需的帧速率流动。
流:
[setup js sprite objects and html element object references]
[setup RAF and SI single-object update functions]
[start SI at percieved/ideal frame-rate]
[iterate through js objects, update data transform string for each]
[loop back to SI]
[start RAF loop]
[iterate through js objects, read object's transform string and assign it to it's html element]
[loop back to RAF]
方法2.将SI放在网络工作者中。这是FAAAST,顺利!
与方法1相同,但将SI放入Web-worker。然后,它将在完全独立的线程上运行,从而使页面仅处理RAF和UI。将子画面数组作为“可传递对象”来回传递。这是buko快。克隆或序列化不需要花时间,但是这不像通过引用传递那样,因为另一侧的引用已被破坏,因此您需要使两侧都传递到另一侧,并且仅在存在时更新它们,进行排序就像在高中时与女友来回传递便条一样。
一次只能读写。只要他们检查它是否未定义以避免错误,就可以了。皇家空军是FAST,将立即将其踢回,然后经过一堆GPU帧,只是检查它是否已被送回。网络工作者中的SI大部分时间都将具有sprite数组,并将更新位置,运动和物理数据,并创建新的转换字符串,然后将其传递回页面中的RAF。
这是我知道的通过脚本对元素进行动画处理的最快方法。这两个函数将作为两个单独的程序在两个单独的线程上运行,并利用多核CPU的优势,而单个js脚本则不会。多线程javascript动画。
而且它可以平稳地运行,而不会出现抖动,但以实际指定的帧速率,几乎没有差异。
结果:
这两种方法中的任何一种都将确保您的脚本在任何PC,电话,平板电脑等上以相同的速度运行(当然,在设备和浏览器的功能范围内)。
requestAnimationFrame
是(如名称所示)仅在需要时才请求动画帧。假设您显示一个静态的黑色画布,由于不需要新的帧,因此应该获得0 fps。但是,如果要显示要求60fps的动画,则也应该这样做。rAF
只允许“跳过”无用的帧,然后节省CPU。