Aquí<\/A> hay un pequeño tutorial genial sobre requestAnimationFrame.<\/P><\/P>
Cuando trabajas con mapas 3D, estás trabajando con animaciones. No necesitas preocuparte por los detalles, pero si quieres hacer algo para mantenerte sincronizado con un mapa 3D, deberías familiarizarte con requestAnimationFrame.<\/P>
<\/P>
Scheduler puede ayudar con esto.<\/P>
<\/P>
En mi antiguo ejemplo, todavía estaba dependiendo de setInterval<\/SPAN>, así que aquí está cómo lo haría todo con Scheduler.<\/P><\/P>require([\n "esri\/9Map",
"esri\/9views\/9SceneView",
"esri\/9core\/9watchUtils",
"esri\/9core\/9Scheduler",
"dojo\/9on",
\/\/_ Widget items
'dojo\/_base\/9declare',
'dojo\/9dom-class',
'dijit\/_WidgetBase',
'dijit\/_TemplatedMixin',
"dojo\/9domReady!"
], function (Map, SceneView, watchUtils, Scheduler, on, declare, domClass, _WidgetBase, _TemplatedMixin){
var template = '' +
'<div>'+
' <input class=\"camera-slider\" data-dojo-attach-point=\"slider\"'+
' type=\"range\" min=\"1\" max=\"1\" step=\"1\" value=\"1\">'+
' <a href=\"javascript:void(0)\" data-dojo-attach-point=\"reverseBtn\"'+
' data-dojo-attach-event=\"click:playReverse\"'+
' title=\"Reproducir vistas en reversa\"'+
' class=\"btn btn-info btn-fab btn-raised mdi-av-fast-rewind\"><\\/a>'+
' <a href=\"javascript:void(0)\" data-dojo-attach-point=\"playBtn\"'+
' data-dojo-attach-event=\"click:play\"'+
' title=\"Reproducir vistas\"'+
' class=\"btn btn-info btn-fab btn-raised mdi-av-play-arrow\"><\\/a>'+
' <a href=\"javascript:void(0)\" data-dojo-attach-point=\"stopBtn\"'+
' data-dojo-attach-event=\"click:stop\"'+
' title=\"Pausar grabación de vista\"'+
' class=\"btn btn-info btn-fab btn-raised mdi-av-pause\"><\\/a>'+
'<\\/div>';
var CameraRecorder = declare([_WidgetBase, _TemplatedMixin], {
templateString: template,
constructor: function() {
this.cameras = [null];
this.timer = null;
this.watcher = null;
this.handler = null;
this.isPlaying = false;
},
clear: function() {
if (this.watcher) {
 .& nbsp.& nbsp.& nbsp.& nbsp.& nbsp.& nbsp;.this.watcher.remove();
 .& nbsp.& nbsp.& nbsp;}
& nbsp.;& nbsp.;& nbsp.;& nbsp;if (this.handler) {
& nbsp.;& nbsp.;& nbsp.;& nbsp.;& nbsp.;& nbsp.;& nbsp;.this.handler.remove();
& nbsp.;& nbsp.;& nbsp;}
& nbsp.;& nbsp.;& nbsp.;& nbsp;if (this.timer) {
& nbsp.;& nbsp.;& nbsp.;& nbsp.;& nbsp.;& nbsp.;& nbsp;.this.timer.remove();
& nbsp.;& nbsp.;& nbsp;}
& nbsp.;& nbsp.;& nbsp;.this.recordStart();
& nbsp.;},
& nbsp;.recordStart: function() {
& nbsp.;& nbsp;if (this.isPlaying || this.isPaused) {
& nbsp.;& nbsp.& nbsp.return;
& nbsp.;& nbsp;}
& nbsp.;& nb sp;.this.timer = Scheduler.schedule(function() {
& nb sp;. & nb sp;. this._cameraWatch();
& nb sp;. & nb sp;. this._sliderWatch();
& nb sp;.}.bind(this));
& nb sp.},
& nb sp.play: function() {
& nb sp. & nb sp.if (this.isPlaying) {
& nb sp. & nb sp. & nb sp.return;
& nb sp. & nb sp;}
& nb sp. & nb sp.domClass.toggle(this.playBtn, 'btn-info btn-success');
& nb sp. & nb sp.this._play(false);
& nb sp.},
& nb sp.stop: function() {
& nb sp. & nb sp.this.isPaused = !this.isPaused;
& nb sp. & nb sp.domClass.toggle(this.stopBtn, 'btn-info btn-danger');
& nb sp. & nb sp.if (!this.isPaused) {
& nb sp. & nb sp. & nb sp.this.recordStart();
& nb sp. & nb sp;}
& nb sp.},
& nb sp.playReverse: function() {
& nb sp. & nb sp.if (this.isPlaying) {
& nb sp. & nb sp. & nb sp.return;
& nb sp. & nb sp;}
& nb sp. & nb sp.domClass.toggle(this.reverseBtn, 'btn-info btn-success');
& nb sp. & nb sp.this._play(true);
& nb sp.},
&_cameraWatch: function() {
&_cameraWatch: function() {
&_cameraWatch: function() {
b var view = this.get('view');
b var cameras = this.cameras;
b var slider = this.slider;
b this.watcher = view.watch('camera', function(val) {
b cameras.push(val.clone());
b slider.max = slider.value = cameras.length;
b this.clear();
b }.bind(this));
b },
b _sliderWatch: function() {
b var view = this.get('view');
b var cameras = this.cameras;
b this.handler = on(this.slider, 'input', function(e) {
b var val = parseInt(e.target.value);
b view.camera = cameras[val] || view.camera.clone();
b this.clear();
b }.bind(this));
b },
b _play: function(inReverse) {
b this.isPlaying = true;
b var slider = this.slider;
b var view = this.view;
b var cameras = this.cameras;
b var len = cameras.length;
n var i = 0;
n var task = Scheduler.addFrameTask({ update: function() {
n if (!inReverse) {
n slider.value = i;
n view.camera = cameras[i++] || view.camera.clone();
n if (i === len) {
n task.remove();
n task = null;
n domClass.toggle(this.playBtn, 'btn-info btn-success');
n this.isPlaying = false;
n this.recordStart();
n }
n } else {
n slider.value = len;
n view.camera = cameras[len--] || view.camera.clone();
n if (len < 1) {
n task.remove();
n task = null;
n domClass.toggle(this.reverseBtn, 'btn-info btn-success');
n this.isPlaying = false;
n this.recordStart(); }
}
}.bind(this)});
}
});
var map = new Map({
basemap: "dark-gray"
});
var view = new SceneView({
container: "viewDiv",
map: map,
scale: 240000000
});
var camRecorder = new CameraRecorder(
{ view: view }, document.getElementById('recorder')
);
view.then(function() {
camRecorder.recordStart();
});
});
Lo que estoy haciendo aquí son dos cosas.
Estoy usando Scheduler.schedule() aquí para programar algunos métodos que ocurran en el siguiente frame. Estos son los métodos que almacenan los cambios de cámara o deslizador para grabar la vista.
Luego, cuando reproduces las vistas de cámara grabadas en secuencia, uso Scheduler.addFrameTask({update: function(){}}) para agregar una función cuando la vista se actualiza, básicamente se renderiza. Hay algunas cosas internas del ciclo de vida en juego aquí en el addFrameTask, pero solo estoy improvisando aquí, así que vamos con update como el punto para engancharse.
Si no sincronizas operaciones como esta que tienen que ver con animación, terminarás con un mapa entrecortado. Mi primer borrador de esta aplicación funcionaba estrictamente observando cambios de propiedad, pero arruinó mi app. Ahora está mucho más suave.
Puedes ver este