UNPKG

kmap-ui

Version:

A components of zmap base on vue2.X

275 lines (257 loc) 8.78 kB
/* Copyright (c) 2015 Jean-Marc VIGLINO, released under the CeCILL-B license (http://www.cecill.info/). ol_layer_AnimatedCluster is a vector layer that animate cluster */ import ol_ext_inherits from '../util/ext' import ol_layer_Vector from 'ol/layer/Vector' import ol_source_Vector from 'ol/source/Vector' import ol_Feature from 'ol/Feature' import {easeOut as ol_easing_easeOut} from 'ol/easing' import {buffer as ol_extent_buffer} from 'ol/extent' import ol_geom_Point from 'ol/geom/Point' import ol_render_getVectorContext from '../util/getVectorContext'; /** * A vector layer for animated cluster * @constructor * @extends {ol.layer.Vector} * @param {olx.layer.AnimatedClusterOptions=} options extend olx.layer.Options * @param {Number} options.animationDuration animation duration in ms, default is 700ms * @param {ol.easingFunction} animationMethod easing method to use, default ol.easing.easeOut */ var 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.bind(this)); // Animate the cluster this.on(['precompose','prerender'], this.animate.bind(this)); this.on(['postcompose','postrender'], this.postanimate.bind(this)); }; ol_ext_inherits(ol_layer_AnimatedCluster, ol_layer_Vector); /** save cluster features before change * @private */ ol_layer_AnimatedCluster.prototype.saveCluster = function() { if (this.oldcluster) { 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; } } }; /** * Get the cluster that contains a feature * @private */ ol_layer_AnimatedCluster.prototype.getClusterForFeature = function(f, cluster) { for (var j=0, c; c=cluster[j]; j++) { var features = c.get('features'); if (features && features.length) { for (var k=0, f2; f2=features[k]; k++) { if (f===f2) { return c; } } } } return false; }; /** * Stop animation * @private */ ol_layer_AnimatedCluster.prototype.stopAnimation = function() { this.animation.start = false; this.animation.cA = []; this.animation.cB = []; }; /** * animate the cluster * @private */ ol_layer_AnimatedCluster.prototype.animate = function(e) { var duration = this.get('animationDuration'); if (!duration) return; var resolution = e.frameState.viewState.resolution; var i, c0, 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 (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 || ol_render_getVectorContext(e); 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(); for (i=0, c; c=a.clusters[i]; i++) { var pt = c.f.getGeometry().getCoordinates(); var dx = pt[0]-c.pt[0]; var dy = pt[1]-c.pt[1]; if (a.revers) { pt[0] = c.pt[0] + d * dx; pt[1] = c.pt[1] + d * dy; } else { pt[0] = pt[0] - d * dx; pt[1] = pt[1] - d * dy; } // Draw feature var st = stylefn(c.f, resolution, true); if (!st.length) st = [st]; // If one feature: draw the feature if (c.f.get("features").length===1 && !dx && !dy) { f = c.f.get("features")[0]; } // else draw a point else { var geo = new ol_geom_Point(pt); f = new ol_Feature(geo); } for (var k=0, s; s=st[k]; k++) { // Multi-line text if (s.getText() && /\n/.test(s.getText().getText())) { var offsetX = s.getText().getOffsetX(); var offsetY = s.getText().getOffsetY(); var rot = s.getText().getRotation() || 0; var fontSize = Number((s.getText().getFont() || '10px').match(/\d+/)) * 1.2; var str = s.getText().getText().split('\n') var dl, nb = str.length-1; var s2 = s.clone(); // Draw each lines str.forEach(function(t, i) { if (i==1) { // Allready drawn s2.setImage(); s2.setFill(); s2.setStroke(); } switch (s.getText().getTextBaseline()) { case 'alphabetic': case 'ideographic': case 'bottom': { dl = nb; break; } case 'hanging': case 'top': { dl = 0; break; } default : { dl = nb/2; break; } } s2.getText().setOffsetX(offsetX - Math.sin(rot)*fontSize*(i - dl)); s2.getText().setOffsetY(offsetY + Math.cos(rot)*fontSize*(i - dl)); s2.getText().setText(t); vectorContext.drawFeature(f, s2); }); } else { vectorContext.drawFeature(f, s); } /* OLD VERSION OL < 4.3 // Retina device var ratio = e.frameState.pixelRatio; 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) { // If one feature: draw the feature if (c.f.get("features").length===1 && !dx && !dy) { vectorContext.drawFeature(c.f.get("features")[0], s); } // else draw a point else { vectorContext.setStyle(s); vectorContext.drawGeometry(geo); } } // older version else { vectorContext.setImageStyle(imgs); vectorContext.setTextStyle(s.getText()); vectorContext.drawPointGeometry(geo); } if (imgs) imgs.setScale(sc); */ } } e.context.restore(); // tell ol 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; }; /** * remove clipping after the layer is drawn * @private */ ol_layer_AnimatedCluster.prototype.postanimate = function(e) { if (this.clip_) { e.context.restore(); this.clip_ = false; } }; export default ol_layer_AnimatedCluster