使用requestAnim或TimeOut在动画中滞后FPS

时间:2017-03-03 10:02:33

标签: javascript html5 animation three.js webgl

我正在尝试使用常量fps获得一个好的动画,但没有任何效果。我正在使用threejs,webgl来渲染场景,对于动画循环,我发现了两种方式(有第三种吗?),它是requestAnimationFrame(...)或者是setTimeOut()。两者都不保证fps是常量,但是我通过使用window.performance.now()的timedelta更新对象位置来修复它。但是我仍然可以清楚地看到lagspikes。那么我该如何解决这个问题呢?显然可能是因为像厄运这样的游戏不会滞后。

我的完整src代码示例可以在这里找到:

http://sc2tube.com:8080/test/three.html

相关代码:

function animate() {

requestAnimationFrame( animate );

// calculate how long the last frame was 
var timefix = (window.performance.now() - last)/(1000/30);

last = window.performance.now();

var oldX = object.position.x;
// calculate updateX including the timefix
var updateX = oldX + (10 / 30 * 100) * dx * timefix;

// update the position of the object
object.position.x = updateX;  
// render the scene
renderer.render(scene, camera);

}

worker.js:

self.addEventListener('message', function(e) {

setInterval(function(){ 

    now = self.performance.now()
    timefix = (now - last)/(1000/100);
    last = now;

    x += 5*timefix*dx;

    self.postMessage(x);

}, 1000/100);

}, false);

var test;  
	var dx = 1, dy = 0;
	var speed = 0.5;

	var activeKey = 0;
    // Set up the scene, camera, and renderer as global variables.
    var scene, camera, renderer;

    init();
    animate();

    // Sets up the scene.
    function init() {

      // Create the scene and set the scene size.
      scene = new THREE.Scene();
      var WIDTH = window.innerWidth - 50,
          HEIGHT = 500;

      // Create a renderer and add it to the DOM.
      renderer = new THREE.WebGLRenderer({antialias:true});
      renderer.setSize(WIDTH, HEIGHT);
      document.body.appendChild(renderer.domElement);

      camera = new THREE.OrthographicCamera( 0, WIDTH, 200, -HEIGHT, 1, 1000 );
      
      camera.position.set(0,0,100);
      scene.add(camera);
      console.log(WIDTH);
      
      
      window.addEventListener('resize', function() {
    	  var WIDTH = window.innerWidth - 50,
          HEIGHT = window.innerHeight - 50;
        renderer.setSize(WIDTH, HEIGHT);
        camera.aspect = WIDTH / HEIGHT;
        camera.updateProjectionMatrix();
        
      });
      renderer.setClearColor();

      var loader = new THREE.ObjectLoader();
  	  
      loader.parse({
    "metadata" : {
        "type" : "Object",
        "version" : 4.3,
        "generator" : "Blender Script"
    },
    "object" : {
        "name" : "red_cube.Material",
        "type" : "Mesh",
        "uuid" : "6071e8f2-79ae-5660-8d2b-aa675c566703",
        "matrix" : [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
        "geometry" : "5d6cbd93-cf58-58a9-b0a7-5be9e5794547",
        "material" : "5e847bd4-84a9-5d4b-a8fb-c567e27f7561"
    },
    "geometries" : [{
            "name" : "red_cube.Material",
            "type" : "BufferGeometry",
            "uuid" : "5d6cbd93-cf58-58a9-b0a7-5be9e5794547",
            "data" : {
                "attributes" : {
                    "position" : {
                        "type" : "Float32Array",
                        "itemSize" : 3,
                        "array" : [0.79906648,-0.73424673,-0.87263167,0.79906648,-0.73424661,1.1273682,-1.2009337,-0.73424661,1.1273681,-1.2009332,-0.73424673,-0.87263215,0.79906696,1.2657533,-0.87263131,-1.2009335,1.2657533,-0.87263179,-1.2009339,1.2657533,1.1273677,0.79906583,1.2657533,1.1273688,0.79906648,-0.73424673,-0.87263167,0.79906696,1.2657533,-0.87263131,0.79906583,1.2657533,1.1273688,0.79906648,-0.73424661,1.1273682,0.79906648,-0.73424661,1.1273682,0.79906583,1.2657533,1.1273688,-1.2009339,1.2657533,1.1273677,-1.2009337,-0.73424661,1.1273681,-1.2009337,-0.73424661,1.1273681,-1.2009339,1.2657533,1.1273677,-1.2009335,1.2657533,-0.87263179,-1.2009332,-0.73424673,-0.87263215,0.79906696,1.2657533,-0.87263131,0.79906648,-0.73424673,-0.87263167,-1.2009332,-0.73424673,-0.87263215,-1.2009335,1.2657533,-0.87263179]
                    },
                    "normal" : {
                        "type" : "Float32Array",
                        "itemSize" : 3,
                        "array" : [-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,0,1,0,0,1,0,0,1,0,0,1,0,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1]
                    },
                    "index" : {
                        "type" : "Uint32Array",
                        "itemSize" : 1,
                        "array" : [0,1,2,2,3,0,4,5,6,6,7,4,8,9,10,10,11,8,12,13,14,14,15,12,16,17,18,18,19,16,20,21,22,22,23,20]
                    }
                }
            }
        }],
    "materials" : [{
            "name" : "Material",
            "type" : "MeshBasicMaterial",
            "uuid" : "5e847bd4-84a9-5d4b-a8fb-c567e27f7561",
            "transparent" : false,
            "opacity" : 1,
            "color" : 10682379
        }]
}, function(object){
       		test = object;
       		object.scale.set(50,50,50);
       		scene.add(object)
       		
      });
      
  	document.addEventListener('keydown', function(e) {
  	    if (activeKey == e.keyCode) return;
  	    activeKey = e.keyCode;
  	    
  	    //left
  	    if (e.keyCode == 37) {
  	        dx = -1;
  	    }
  	    //top
  	    else if (e.keyCode == 38) {
  	        dy = 1;
  	    }
  	    //right
  	    else if (e.keyCode == 39) {
  	        dx = 1;
  	    }
  	    //bottom
  	    else if (e.keyCode == 40) {
  	        dy = -1;
  	    }
  	});
  	document.addEventListener('keyup', function(e) {
  	    switch (e.keyCode) {
  	        case 37: // left
  	        case 39: // right
  	            dx = 0;
  	            break;
  	            
  	        case 38: // up
  	        case 40: // down
  	            dy = 0;
  	            break;
  	    }
  	    
  	    activeKey = 0;
  	});

    }
    
    var start;
    var last;

    function animate() {

        requestAnimationFrame( animate );
                
    	if(start == null) {
    		start = window.performance.now();
    		last = start;
    	}
    	
    	var timefix = (window.performance.now() - last)/(1000/30);
    	
    	last = window.performance.now();
    	
        if(test != null) {
       	    var oldX = test.position.x;
       	    var oldY = test.position.y;
       	    
       	    var updateX = oldX + (10 / 30 * 100) * dx * speed * timefix;
       	    var updateY = oldY + (10 / 30 * 100) * dy * speed * timefix;  
       	    
       	    if(updateX > 1800 ) {
       	    	dx = -1;
       	    } else if(updateX < 100) {       	    	
       	    	dx = 1;
       	    }
       	    
       	    test.position.x = updateX;  
       	    test.position.y =  oldY + (10 / 30 * 100) * dy * speed * timefix;  
       	    
       	 	var text = document.getElementById('panel');
       	 	text.innerHTML =  timefix;
       		renderer.render(scene, camera);
       	}

    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js"></script>
<body style="margin: 0;">

<div id="panel">TEST
</div>
<br>

</body>

2 个答案:

答案 0 :(得分:1)

问题在于游戏滴答声。

你需要的是线程。 一个渲染线程。 一个游戏线程。

对于游戏主题我建议一个webworker:

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

让游戏线程每隔50ms运行一次以更新游戏逻辑。它应该睡觉&#34;插图中。 你将东西发布回渲染线程,它会更新所有东西,并在50毫秒内将当前事件的轨迹提供给下一个pos。

  • tick 1. 0MS

    • 游戏主题:
      • 在0.0
      • 生成块
      • 将游戏状态推送到渲染线程
    • 渲染线程
      • 收集实体。
      • 如果移动,则更新位置实体
      • 在pos
      • 处绘制实体
  • tick 2. 50ms

    • 游戏主题:
      • 获取实体
      • 触发更新功能
        • 红色区块左移20
        • 将游戏状态推送到渲染线程
    • 渲染线程
      • 收集实体。
      • 如果移动,则更新位置的实体
        • 红色块从0移动到20
        • avg frames per tick 4
        • 每次嘀嗒声的相互作用,5 px。
        • 将红色更新为5
      • 在pos
      • 处绘制实体

修改 添加了如何利用渲染线程的示例代码。

基本上,游戏线程(webworker)中的对象与渲染线程中的对象相同。 唯一的区别是,渲染线程有渲染指令(onRender),游戏循环有更新指令(更新时)

所以它们是相同的,但也不同。

看看。

&#13;
&#13;
function getInlineJS() {
    var js = document.querySelector('[type="javascript/worker"]').textContent;
    var blob = new Blob([js], {"type": "text\/plain"});
    return URL.createObjectURL(blob);
}



var RedCube = function(id) {
    this.cube = null;
    this.type = 'redcube';
    if(typeof id === undefined) {
       this.entityId = generateId();
    }
    else {
        this.entityId = id;
    }
    this.lastX = 0;
    this.x = 0;
}
RedCube.prototype.getType = function() {
   return this.type;
}
RedCube.prototype.onUpdate = function() {
    this.x += 20;
}
RedCube.prototype.loadCube = function(scene, renderer) {
var that = this;
       
        var loader = new THREE.ObjectLoader();
  	        loader.parse({
    "metadata" : {
        "type" : "Object",
        "version" : 4.3,
        "generator" : "Blender Script"
    },
    "object" : {
        "name" : "red_cube.Material",
        "type" : "Mesh",
        "uuid" : "6071e8f2-79ae-5660-8d2b-aa675c566703",
        "matrix" : [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
        "geometry" : "5d6cbd93-cf58-58a9-b0a7-5be9e5794547",
        "material" : "5e847bd4-84a9-5d4b-a8fb-c567e27f7561"
    },
    "geometries" : [{
            "name" : "red_cube.Material",
            "type" : "BufferGeometry",
            "uuid" : "5d6cbd93-cf58-58a9-b0a7-5be9e5794547",
            "data" : {
                "attributes" : {
                    "position" : {
                        "type" : "Float32Array",
                        "itemSize" : 3,
                        "array" : [0.79906648,-0.73424673,-0.87263167,0.79906648,-0.73424661,1.1273682,-1.2009337,-0.73424661,1.1273681,-1.2009332,-0.73424673,-0.87263215,0.79906696,1.2657533,-0.87263131,-1.2009335,1.2657533,-0.87263179,-1.2009339,1.2657533,1.1273677,0.79906583,1.2657533,1.1273688,0.79906648,-0.73424673,-0.87263167,0.79906696,1.2657533,-0.87263131,0.79906583,1.2657533,1.1273688,0.79906648,-0.73424661,1.1273682,0.79906648,-0.73424661,1.1273682,0.79906583,1.2657533,1.1273688,-1.2009339,1.2657533,1.1273677,-1.2009337,-0.73424661,1.1273681,-1.2009337,-0.73424661,1.1273681,-1.2009339,1.2657533,1.1273677,-1.2009335,1.2657533,-0.87263179,-1.2009332,-0.73424673,-0.87263215,0.79906696,1.2657533,-0.87263131,0.79906648,-0.73424673,-0.87263167,-1.2009332,-0.73424673,-0.87263215,-1.2009335,1.2657533,-0.87263179]
                    },
                    "normal" : {
                        "type" : "Float32Array",
                        "itemSize" : 3,
                        "array" : [-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,-1.0658141e-14,-1,5.9604645e-08,0,1,0,0,1,0,0,1,0,0,1,0,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,1,4.4703416e-08,2.8312209e-07,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-2.9802322e-07,-5.9604723e-08,1,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,-1,-1.1920929e-07,-2.3841858e-07,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1,2.3841858e-07,1.7881393e-07,-1]
                    },
                    "index" : {
                        "type" : "Uint32Array",
                        "itemSize" : 1,
                        "array" : [0,1,2,2,3,0,4,5,6,6,7,4,8,9,10,10,11,8,12,13,14,14,15,12,16,17,18,18,19,16,20,21,22,22,23,20]
                    }
                }
            }
        }],
    "materials" : [{
            "name" : "Material",
            "type" : "MeshBasicMaterial",
            "uuid" : "5e847bd4-84a9-5d4b-a8fb-c567e27f7561",
            "transparent" : false,
            "opacity" : 1,
            "color" : 10682379
        }]
}, function(object){
       		that.cube = object;
       		object.scale.set(50,50,50);
       		scene.add(object)
       		
      });
}
RedCube.prototype.onRender = function(scene, renderer) {
   if(this.cube === null) {
       this.loadCube(scene, renderer);
   }
   
   // Some interprolation logic here to move from lastpos to next pos in average frames
   // per tick.
   this.cube.position.x = this.x;
}

RedCube.prototype.getType = function() {
   return type;
}

RedCube.prototype.generateSyncPacket = function() {
   return {
            type: this.getType(),
            x : this.x
          };
}

RedCube.prototype.parseSyncPacket = function(syncpacket) {
   this.setPosition(syncpacket.x);
}

RedCube.prototype.generateId = function() {
    var d = new Date().getTime();
    if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
        d += performance.now(); //use high-precision timer if available
    }
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });

}


RedCube.prototype.getEntityId = function() {
    return this.entityId;
}
RedCube.prototype.beforeDeath = function() {
}

RedCube.prototype.die = function() {
}

RedCube.prototype.setPosition = function(newpos) {
    this.lastX = this.x;
    this.x = newpos;
}

RedCube.prototype.getPosition = function() {
    return this.x;
}
RedCube.prototype.getLastX = function() {
    return this.lastX;
}

var EntityRegistry = function() {
   this.entities = {};
   this.types = {};
}

EntityRegistry.prototype.register = function(entity) {
   this.entities[entity.getEntityId()] = entity;
   
}
EntityRegistry.prototype.callUpdate = function() {
  for(entityId in this.entities) {
      if(this.entities.hasOwnProperty(entityId)) {
          this.entities[entityId].onUpdate();
      }
   }
}
EntityRegistry.prototype.callOnRender = function(scene, renderer) {
  for(entityId in this.entities) {
      if(this.entities.hasOwnProperty(entityId)) {
          this.entities[entityId].onRender(scene, renderer);
      }
   }
}
EntityRegistry.prototype.remove = function(entity) {
   entity.beforeDeath();
   delete this.entities[entity.getEntityId()]
   entity.die();
}

EntityRegistry.prototype.registerType = function(name, entityClass) {
    this.types[name] = entityClass;
}
EntityRegistry.prototype.startEntity = function(syncpacket, entityId) {
    var entity = new this.types[syncpacket.type](entityId);
    entity.parseSyncPacket(syncpacket);
    this.register(entity);
}

EntityRegistry.prototype.getSyncData = function() {
   var syncpacket = {};
   for(entityId in this.entities) {
      if(this.entities.hasOwnProperty(entityId)) {
          syncpacket[entityId] = this.entities[entityId].generateSyncPacket();
      }
   }
   return syncpacket;
}

EntityRegistry.prototype.parseSyncData = function(syncpacket) {
   for(entityId in syncpacket) {
      
      if(this.entities.hasOwnProperty(entityId)) {
          this.entities[entityId].parseSyncPacket(syncpacket[entityId]);
      }
      else {
          this.startEntity(syncpacket[entityId], entityId);
      }
   }
   return syncpacket;
}
var REGISTRY = new EntityRegistry();
REGISTRY.registerType('redcube', RedCube);

 	var test = "d";  
	var dx = 1, dy = 0;
	var speed = 0.5;

	var activeKey = 0;
    // Set up the scene, camera, and renderer as global variables.
    var scene, camera, renderer;

    var worker = new Worker(getInlineJS());
    worker.postMessage("dasd");
  
    worker.addEventListener('message', function(e) {
    	  REGISTRY.parseSyncData(e.data);
    	}, false);
    
    
    console.log("asd " + test);
    init();
    animate();

    // Sets up the scene.
    function init() {

      // Create the scene and set the scene size.
      scene = new THREE.Scene();
      var WIDTH = window.innerWidth - 50,
          HEIGHT = 500;

      // Create a renderer and add it to the DOM.
      renderer = new THREE.WebGLRenderer({antialias:true});
      renderer.setSize(WIDTH, HEIGHT);
      document.body.appendChild(renderer.domElement);

      camera = new THREE.OrthographicCamera( 0, WIDTH, 200, -HEIGHT, 1, 1000 );
      
      camera.position.set(0,0,100);
      scene.add(camera);
      console.log(WIDTH);
      
      
      window.addEventListener('resize', function() {
    	  var WIDTH = window.innerWidth - 50,
          HEIGHT = window.innerHeight - 50;
        renderer.setSize(WIDTH, HEIGHT);
        camera.aspect = WIDTH / HEIGHT;
        camera.updateProjectionMatrix();
        
      });
      renderer.setClearColor();

     
      
  	document.addEventListener('keydown', function(e) {
  	    if (activeKey == e.keyCode) return;
  	    activeKey = e.keyCode;
  	    
  	    //left
  	    if (e.keyCode == 37) {
  	        dx = -1;
  	    }
  	    //top
  	    else if (e.keyCode == 38) {
  	        dy = 1;
  	    }
  	    //right
  	    else if (e.keyCode == 39) {
  	        dx = 1;
  	    }
  	    //bottom
  	    else if (e.keyCode == 40) {
  	        dy = -1;
  	    }
  	});
  	document.addEventListener('keyup', function(e) {
  	    switch (e.keyCode) {
  	        case 37: // left
  	        case 39: // right
  	            dx = 0;
  	            break;
  	            
  	        case 38: // up
  	        case 40: // down
  	            dy = 0;
  	            break;
  	    }
  	    
  	    activeKey = 0;
  	});

    }
    
    var start;
    var last;

    var timefix, oldX,oldY, updateX,updateY,text;
    
    function animate() {
    	
    	  REGISTRY.callOnRender(scene, renderer);
        renderer.render(scene, camera);   	   
        requestAnimationFrame( animate );

    }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js"></script>
<body style="margin: 0;">

<div id="panel">TEST
</div>
<br>
<script type="javascript/worker">
var RedCube = function(id) {
    this.direction = false;
    this.type = 'redcube';
    if(typeof id === 'undefined') {
       this.entityId = this.generateId();
    }
    else {
        this.entityId = id;
    }
    this.lastX = 0;
    this.x = 0;
}
RedCube.prototype.getType = function() {
   return this.type;
}

RedCube.prototype.generateSyncPacket = function() {
   return {
            type: this.getType(),
            x : this.x
          };
}

RedCube.prototype.parseSyncPacket = function(syncpacket) {
   this.setPosition(syncpacket.x);
}

RedCube.prototype.generateId = function() {
    var d = new Date().getTime();
    if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
        d += performance.now(); //use high-precision timer if available
    }
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });

}

RedCube.prototype.getEntityId = function() {
    return this.entityId;
}

RedCube.prototype.beforeDeath = function() {
}

RedCube.prototype.die = function() {
}

RedCube.prototype.setPosition = function(newpos) {
    this.lastX = this.x;
    this.x = newpos;
}

RedCube.prototype.getPosition = function() {
    return this.x;
}
RedCube.prototype.getLastX = function() {
    return this.lastX;
}

var EntityRegistry = function() {
   this.entities = {};
   this.types = {};
}

RedCube.prototype.onUpdate = function() {
    if(this.x > 500) {
       this.direction = true;
    }
    if(this.x <= 0) {
       this.direction = false;
    }
    this.x += !this.direction ? 20 : -20;
}
RedCube.prototype.onRender = function(scene, renderer) {
/// this is not a rendering thread. leave it empty
}
EntityRegistry.prototype.register = function(entity) {   
   this.entities[entity.getEntityId()] = entity;
   
}

EntityRegistry.prototype.remove = function(entity) {
   entity.beforeDeath();
   delete this.entities[entity.getEntityId()]
   entity.die();
}

EntityRegistry.prototype.registerType = function(name, entityClass) {
    this.types[name] = entityClass;
}
EntityRegistry.prototype.startEntity = function(entityId, syncpacket) {
    var entity = this.types[syncpacket.type](entityId);
    entity.parseSyncPacket(syncpacket);
    this.register(entity);
}

EntityRegistry.prototype.getSyncData = function() {
   var syncpacket = {};
   
   for(entityId in this.entities) {
      
      if(this.entities.hasOwnProperty(entityId)) {
          syncpacket[entityId] = this.entities[entityId].generateSyncPacket();
      }
   }
   return syncpacket;
}
EntityRegistry.prototype.callUpdate = function() {
  for(entityId in this.entities) {
      if(this.entities.hasOwnProperty(entityId)) {
          this.entities[entityId].onUpdate();
      }
   }
}
EntityRegistry.prototype.callOnRender = function(scene, renderer) {
  for(entityId in this.entities) {
      if(this.entities.hasOwnProperty(entityId)) {
          this.entities[entityId].onRender(scene, renderer);
      }
   }
}
EntityRegistry.prototype.parseSyncData = function(syncpacket) {
   for(entityId in syncpacket) {
      if(this.entities.hasOwnProperty(entityId)) {
          this.entities[entityId].parseSyncPacket(syncpacket[entityid]);
      }
      else {
          this.startEntity(syncpacket, entityId);
      }
   }
   return syncpacket;
}
var REGISTRY = new EntityRegistry();
var little_red = new RedCube();

REGISTRY.register(little_red);    

var x = 0;
var timefix = 0;
var last = 0;
var dx = 1;
var loopInterval = 0;
loopInterval = setInterval(function(){ 			
    REGISTRY.callUpdate()
		var msg = REGISTRY.getSyncData();
		self.postMessage(msg);
		
	}, 1000/60);
  
self.addEventListener('message', function(e) {
	  
	

}, false);
</script>
</body>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

你应该能够毫无问题地使用requestAnimationFrame 我认为你的滞后问题源于垃圾收集 在您的动画功能中,您正在声明新变量(即使它们具有相同的名称)。尝试在animate函数之外声明这些,并在animate函数中更新它们。

这只是我的猜测,也是我开始寻找问题的第一个地方。

enter image description here