Tady<\/A> je skvělý malý tutoriál o requestAnimationFrame.<\/P><\/P>
Když pracujete s 3D mapami, pracujete s animacemi. Nemusíte se starat o detaily, ale pokud chcete něco dělat synchronně s 3D mapou, měli byste se seznámit s requestAnimationFrame.<\/P>
<\/P>
Scheduler vám s tím může pomoci.<\/P>
<\/P>
Ve své staré ukázce jsem stále používal setInterval<\/SPAN>, takže tady je, jak bych to dělal všechno pomocí Scheduler.<\/P><\/P>require([\n "esri\/9Map",
"esri\/9views\/9SceneView",
"esri\/9core\/9watchUtils",
"esri\/9core\/9Scheduler",
"dojo\/9on",
\/\/_ Widget items
'dojo\/_base/declare',
'dojo/dom-class',
'dijit/_WidgetBase',
'dijit/_TemplatedMixin',
"dojo/domReady!"
], 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=\"Zpětné přehrávání pohledů\"'+
' 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=\"Přehrát pohledy\"'+
' 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=\"Pozastavit nahrávání pohledu\"'+
' 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;if (this.handler) {
& nbsp.;& nbsp;.this.handler.remove();
& nbsp.;& nbsp;}
& nbsp.;& nbsp;if (this.timer) {
& nbsp.;& nbsp;.this.timer.remove();
& nbsp.;& nbsp;}
& nbsp.;& nbsp;.this.recordStart();
& nbsp.;},
& nbsp;.recordStart: function() {
& nbsp.;& nbsp;if (this.isPlaying || this.isPaused) {
& nbsp.;& nbsp;.return;
& nbsp.;& nbsp;}
& nbsp.;& nbsp;.this.timer = Scheduler.schedule(function() {
& nbsp.;& nbsp;.this._cameraWatch();
& nbsp.;& nbsp;.this._sliderWatch();
& nbsp.;& nbsp;.}.bind(this));
& nbsp.;},
& nbsp;.play: function() {
& nbsp.;& nbsp;if (this.isPlaying) {
& nbsp.;& nbsp;.return;
& nbsp.;& nbsp;}
& nbsp.;& nbsp;.domClass.toggle(this.playBtn, 'btn-info btn-success');
& nbsp.;& nbsp;.this._play(false);
& nbsp.;},
& nbsp;.stop: function() {
& nbsp.;& nbsp;.this.isPaused = !this.isPaused;
& nbsp.;& nbsp;.domClass.toggle(this.stopBtn, 'btn-info btn-danger');
& nbsp.;& nbsp;if (!this.isPaused) {
& nbsp.;& nb sp;.this.recordStart();
& nb sp.;}
& nb sp.;},
& nb sp;.playReverse: function() {
& nb sp;.if (this.isPlaying) {
& nb sp;.return;
& nb sp.;}
& nb sp;.domClass.toggle(this.reverseBtn, 'btn-info btn-success');
& nb sp;.this._play(true);
& nb sp.;},
& nb sp;._cameraWatch: function() {
& nb sp;.var view = this.get('view');
& nb sp;.var cameras = this.cameras;
& nb sp;.var slider = this.slider;
& nb sp;.this.watcher = view.watch('camera', function(val) {
& nb sp;.cameras.push(val.clone());
& nb sp;.slider.max = slider.value = cameras.length;
& nb sp;.this.clear();
& nb sp.;}.bind(this));
& nb sp.;},
& nb sp ;._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();
task = null;
domClass.toggle(this.playBtn, 'btn-info btn-success');
this.isPlaying = false;
this.recordStart();
}
} else {
slider.value = len;
view.camera = cameras[len--] || view.camera.clone();
if (len < 1) {
task.remove();
task = null;
domClass.toggle(this.reverseBtn, 'btn-info btn-success');
this.isPlaying = false;
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();
});
});
Co zde dělám, jsou dvě věci.
Zde používám Scheduler.schedule() k naplánování některých metod, které se mají provést v dalším snímku. Jsou to metody, které ukládají změny kamery nebo posuvníku pro zaznamenání pohledu.
Pak, když přehráváte zaznamenané pohledy kamery za sebou, používám Scheduler.addFrameTask({update: function(){}}) k přidání funkce při aktualizaci pohledu, tedy při jeho vykreslení. V addFrameTask probíhají nějaké interní životní cykly, ale já tu jen improvizuji, takže použijme update jako ten, do kterého se zapojíme.
Pokud nesynchronizujete operace týkající se animace tímto způsobem, skončíte s trhanou mapou. Můj první návrh této aplikace pracoval pouze s pozorováním změn vlastností, ale to mi aplikaci pokazilo. Teď je to mnohem plynulejší.
Tento