Aqui<\/A> está um tutorial legal sobre requestAnimationFrame.<\/P><\/P>
Quando você trabalha com mapas 3D, você está trabalhando com animações. Você não precisa se preocupar com os detalhes, mas se quiser fazer algo para ficar sincronizado com um mapa 3D, deve se familiarizar com requestAnimationFrame.<\/P>
<\/P>
Scheduler pode ajudar com isso.<\/P>
<\/P>
No meu exemplo antigo, eu ainda estava dependendo de setInterval<\/SPAN>, então aqui está como eu faria tudo com Scheduler.<\/P><\/P>require([\n "esri\/Map",\n "esri\/views\/SceneView",\n "esri\/core\/watchUtils",\n "esri\/core\/Scheduler",\n "dojo\/on",\n \/\/_ Widget items\n 'dojo\/_base\/declares',\n 'dojo\/dom-class',\n 'dijit\/_WidgetBase',\n 'dijit\/_TemplatedMixin',\n "dojo\/domReady!"\n], function (Map, SceneView, watchUtils, Scheduler, on, declare, domClass, _WidgetBase, _TemplatedMixin){\n\n var template = '' +\n '<div>'+\n ' <input class=\"camera-slider\" data-dojo-attach-point=\"slider\"'+\n ' type=\"range\" min=\"1\" max=\"1\" step=\"1\" value=\"1\">'+\n ' <a href=\"javascript:void(0)\" data-dojo-attach-point=\"reverseBtn\"'+\n ' data-dojo-attach-event=\"click:playReverse\"'+\n ' title=\"Reproduzir views ao contrário\"'+\n ' class=\"btn btn-info btn-fab btn-raised mdi-av-fast-rewind\"><\\/a>'+\n ' <a href=\"javascript:void(0)\" data-dojo-attach-point=\"playBtn\"'+\n ' data-dojo-attach-event=\"click:play\"'+\n ' title=\"Reproduzir views\"'+\n ' class=\"btn btn-info btn-fab btn-raised mdi-av-play-arrow\"><\\/a>'+\n ' <a href=\"javascript:void(0)\" data-dojo-attach-point=\"stopBtn\"'+\n ' data-dojo-attach-event=\"click:stop\"'+\n ' title=\"Pausar gravação da view\"'+\n ' class=\"btn btn-info btn-fab btn-raised mdi-av-pause\"><\\/a>'+
'<˽iv>' ;
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) {
this.watcher.remove();
}
if (this.handler) {
this.handler.remove();
}
if (this.timer) {
this.timer.remove();
}
this.recordStart();
},
recordStart: function() {
if (this.isPlaying || this.isPaused) {
return;
}
this.timer = Scheduler.schedule(function() {
this._cameraWatch();
this._sliderWatch();
}.bind(this));
},
play: function() {
if (this.isPlaying) {
return;
}
domClass.toggle(this.playBtn, 'btn-info btn-success');
this._play(false);
},
stop: function() {
this.isPaused = !this.isPaused;
domClass.toggle(this.stopBtn, 'btn-info btn-danger');
if (!this.isPaused) {
this.recordStart();
}
},
playReverse: function() {
if (this.isPlaying) {
return;
}
domClass.toggle(this.reverseBtn, 'btn-info btn-success');
this._play(true);
},
_cameraWatch: function() {
var view = this.get('view');
var cameras = this.cameras;
var slider = this.slider;
this.watcher = view.watch('camera', function(val) {
cameras.push(val.clone());
slider.max = slider.value = cameras.length;
this.clear();
}.bind(this));
},
_sliderWatch: function() {
var view = this.get('view');
var cameras = this.cameras;
this.handler = on(this.slider, 'input', function(e) {
var val = parseInt(e.target.value);
view.camera = cameras[val] || view.camera.clone();
this.clear();
}.bind(this));
},
_play: function(inReverse) {
this.isPlaying = true;
var slider = this.slider;
var view = this.view;
var cameras = this.cameras;
var len = cameras.length;
var i = 0;
var task = Scheduler.addFrameTask({ update: function() {
if (!inReverse) {
slider.value = i;
view.camera = cameras[i++] || view.camera.clone();
if (i === len) {
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();
});
});
O que estou fazendo aqui são duas coisas.
Estou usando Scheduler.schedule() aqui para agendar alguns métodos para ocorrer no próximo frame. Estes são os métodos que armazenam as mudanças da câmera ou do slider para gravar a visualização.
Então, quando você reproduz as visualizações de câmera gravadas em sequência, eu uso Scheduler.addFrameTask({update: function(){}}) para adicionar uma função quando a visualização é atualizada, basicamente renderizada. Há algumas coisas internas de ciclo de vida em funcionamento aqui no addFrameTask, mas estou apenas chutando aqui, então vamos usar update como o ponto para conectar.
Se você não sincronizar operações como esta que têm a ver com animação, você acabará com um mapa travado. Meu primeiro rascunho deste app funcionava estritamente observando mudanças de propriedade, mas isso prejudicou meu app. Agora está muito mais suave.
Você pode ver este