Hier<\/A> is een coole kleine tutorial over requestAnimationFrame.<\/P><\/P>
Als je werkt met 3D-kaarten, werk je met animaties. Je hoeft je niet druk te maken over de details, maar als je iets wilt doen om synchroon te blijven met een 3D-kaart, moet je vertrouwd raken met requestAnimationFrame.<\/P>
<\/P>
Scheduler kan hierbij helpen.<\/P>
<\/P>
In mijn oude voorbeeld gebruikte ik nog steeds setInterval<\/SPAN>, dus hier is hoe ik het allemaal zou doen met 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\/_declare',\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=\"Speel views achteruit af\"'+\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=\"Speel views af\"'+\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=\"Opname pauzeren view afspelen \u2013 pauzeer opname view \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen \u2013 opname pauzeren view afspelen "+class+"></a>");
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));
},
s _play: function(inReverse) {
s this.isPlaying = true;
s var slider = this.slider;
s var view = this.view;
s var cameras = this.cameras;
s var len = cameras.length;
s
var i = 0;
s var task = Scheduler.addFrameTask({ update: function() {
s if (!inReverse) {
s slider.value = i;
s view.camera = cameras[i++] || view.camera.clone();
s if (i === len) {
s task.remove();
s task = null;
s domClass.toggle(this.playBtn, 'btn-info btn-success');
s this.isPlaying = false;
s this.recordStart();
s }
s } else {
s slider.value = len;
s view.camera = cameras[len--] || view.camera.clone();
s if (len < 1) {
s task.remove();
s task = null;
s domClass.toggle(this.reverseBtn, 'btn-info btn-success');
s this.isPlaying = false;
s 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();
});
});
Wat ik hier doe zijn twee dingen.
Ik gebruik hier Scheduler.schedule() om enkele methoden in te plannen die in het volgende frame moeten plaatsvinden. Dit zijn de methoden die de camera- of schuifregelaarwijzigingen opslaan om de weergave op te nemen.
Vervolgens, wanneer je de opgenomen camerabeelden achter elkaar afspeelt, gebruik ik Scheduler.addFrameTask({update: function(){}}) om een functie toe te voegen wanneer de weergave wordt bijgewerkt, in feite gerenderd. Er is wat interne lifecycle-logica aan het werk in de addFrameTask, maar ik schiet hier gewoon uit de heup, dus laten we update gebruiken als het punt om op aan te haken.
Als je operaties zoals deze die met animatie te maken hebben niet synchroniseert, krijg je een haperende kaart. Mijn eerste versie van deze app werkte strikt door te luisteren naar eigenschapswijzigingen, maar dat verpestte mijn app. Nu is het veel vloeiender.
Je kunt dit