| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /*
- Copyright (c) 2015 Jean-Marc VIGLINO,
- released under the CeCILL-B license (http://www.cecill.info/).
- github: https://github.com/Viglino/OL3-AnimatedCluster
- ol.layer.AnimatedCluster is a vector layer tha animate cluster
-
- olx.layer.AnimatedClusterOptions: extend olx.layer.Options
- { animationDuration {Number} animation duration in ms, default is 700ms
- animationMethod {function} easing method to use, default ol.easing.easeOut
- }
- */
- /**
- * @constructor AnimatedCluster
- * @extends {ol.layer.Vector}
- * @param {olx.layer.AnimatedClusterOptions=} options
- * @todo
- */
- ol.layer.AnimatedCluster = function(opt_options)
- { var options = opt_options || {};
- ol.layer.Vector.call (this, options);
-
- this.oldcluster = new ol.source.Vector();
- this.clusters = [];
- this.animation={start:false};
- this.set('animationDuration', typeof(options.animationDuration)=='number' ? options.animationDuration : 700);
- this.set('animationMethod', options.animationMethod || ol.easing.easeOut);
- // Save cluster before change
- this.getSource().on('change', this.saveCluster, this);
- // Animate the cluster
- this.on('precompose', this.animate, this);
- this.on('postcompose', this.postanimate, this);
- };
- ol.inherits (ol.layer.AnimatedCluster, ol.layer.Vector);
- /** @private save cluster features before change
- */
- ol.layer.AnimatedCluster.prototype.saveCluster = function()
- { this.oldcluster.clear();
- if (!this.get('animationDuration')) return;
- var features = this.getSource().getFeatures();
- if (features.length && features[0].get('features'))
- { this.oldcluster.addFeatures (this.clusters);
- this.clusters = features.slice(0);
- this.sourceChanged = true;
- }
- };
- /** @private Get the cluster that contains a feature
- */
- ol.layer.AnimatedCluster.prototype.getClusterForFeature = function(f, cluster)
- { for (var j=0, c; c=cluster[j]; j++)
- { var features = cluster[j].get('features');
- if (features && features.length)
- { for (var k=0, f2; f2=features[k]; k++)
- { if (f===f2)
- { return cluster[j];
- }
- }
- }
- }
- return false;
- };
- /** @private
- */
- ol.layer.AnimatedCluster.prototype.stopAnimation = function()
- { this.animation.start = false;
- this.animation.cA = [];
- this.animation.cB = [];
- };
- /** @private animate the cluster
- */
- ol.layer.AnimatedCluster.prototype.animate = function(e)
- { var duration = this.get('animationDuration');
- if (!duration) return;
- var resolution = e.frameState.viewState.resolution;
- var a = this.animation;
- var time = e.frameState.time;
- // Start a new animation, if change resolution and source has changed
- if (a.resolution != resolution && this.sourceChanged)
- { var extent = e.frameState.extent;
- if (a.resolution < resolution)
- { extent = ol.extent.buffer(extent, 100*resolution);
- a.cA = this.oldcluster.getFeaturesInExtent(extent);
- a.cB = this.getSource().getFeaturesInExtent(extent);
- a.revers = false;
- }
- else
- { extent = ol.extent.buffer(extent, 100*resolution);
- a.cA = this.getSource().getFeaturesInExtent(extent);
- a.cB = this.oldcluster.getFeaturesInExtent(extent);
- a.revers = true;
- }
- a.clusters = [];
- for (var i=0, c0; c0=a.cA[i]; i++)
- { var f = c0.get('features');
- if (f && f.length)
- { var c = this.getClusterForFeature (f[0], a.cB);
- if (c) a.clusters.push({ f:c0, pt:c.getGeometry().getCoordinates() });
- }
- }
- // Save state
- a.resolution = resolution;
- this.sourceChanged = false;
- // No cluster or too much to animate
- if (!a.clusters.length || a.clusters.length>1000)
- { this.stopAnimation();
- return;
- }
- // Start animation from now
- time = a.start = (new Date()).getTime();
- }
- // Run animation
- if (a.start)
- { var vectorContext = e.vectorContext;
- var d = (time - a.start) / duration;
- // Animation ends
- if (d > 1.0)
- { this.stopAnimation();
- d = 1;
- }
- d = this.get('animationMethod')(d);
- // Animate
- var style = this.getStyle();
- var stylefn = (typeof(style) == 'function') ? style : style.length ? function(){ return style; } : function(){ return [style]; } ;
- // Layer opacity
- e.context.save();
- e.context.globalAlpha = this.getOpacity();
- // Retina device
- var ratio = e.frameState.pixelRatio;
- for (var i=0, c; c=a.clusters[i]; i++)
- { var pt = c.f.getGeometry().getCoordinates();
- if (a.revers)
- { pt[0] = c.pt[0] + d * (pt[0]-c.pt[0]);
- pt[1] = c.pt[1] + d * (pt[1]-c.pt[1]);
- }
- else
- { pt[0] = pt[0] + d * (c.pt[0]-pt[0]);
- pt[1] = pt[1] + d * (c.pt[1]-pt[1]);
- }
- // Draw feature
- var st = stylefn(c.f, resolution);
- /* Preserve pixel ration on retina */
- var geo = new ol.geom.Point(pt);
- for (var k=0; s=st[k]; k++)
- { var sc;
- // OL < v4.3 : setImageStyle doesn't check retina
- var imgs = ol.Map.prototype.getFeaturesAtPixel ? false : s.getImage();
- if (imgs)
- { sc = imgs.getScale();
- imgs.setScale(sc*ratio);
- }
- // OL3 > v3.14
- if (vectorContext.setStyle)
- { vectorContext.setStyle(s);
- vectorContext.drawGeometry(geo);
- }
- // older version
- else
- { vectorContext.setImageStyle(imgs);
- vectorContext.setTextStyle(s.getText());
- vectorContext.drawPointGeometry(geo);
- }
- if (imgs) imgs.setScale(sc);
- }
- /*/
- var f = new ol.Feature(new ol.geom.Point(pt));
- for (var k=0; s=st[k]; k++)
- { var imgs = s.getImage();
- var sc = imgs.getScale();
- imgs.setScale(sc*ratio); // drawFeature don't check retina
- vectorContext.drawFeature(f, s);
- imgs.setScale(sc);
- }
- /**/
- }
- e.context.restore();
- // tell OL3 to continue postcompose animation
- e.frameState.animate = true;
- // Prevent layer drawing (clip with null rect)
- e.context.save();
- e.context.beginPath();
- e.context.rect(0,0,0,0);
- e.context.clip();
- this.clip_ = true;
- }
- return;
- };
- /** @private remove clipping after the layer is drawn
- */
- ol.layer.AnimatedCluster.prototype.postanimate = function(e)
- { if (this.clip_)
- { e.context.restore();
- this.clip_ = false;
- }
- };
|