mirror of
https://github.com/Viglino/ol-ext.git
synced 2025-12-08 19:26:29 +00:00
232 lines
7.3 KiB
JavaScript
232 lines
7.3 KiB
JavaScript
/*
|
|
Copyright (c) 2016 Jean-Marc VIGLINO,
|
|
released under the CeCILL license (http://www.cecill.info/).
|
|
*/
|
|
|
|
import ol from 'ol'
|
|
import ol_Object from 'ol/object'
|
|
import ol_easing from 'ol/easing'
|
|
import ol_Map from 'ol/map'
|
|
import ol_layer_Vector from 'ol/layer/vector'
|
|
import ol_extent from 'ol/extent'
|
|
import ol_Observable from 'ol/observable'
|
|
|
|
/** Feature animation base class
|
|
* Use the {@link _ol_Map_#animateFeature} or {@link _ol_layer_Vector_#animateFeature} to animate a feature
|
|
* on postcompose in a map or a layer
|
|
* @constructor
|
|
* @fires animationstart|animationend
|
|
* @param {ol_featureAnimationOptions} options
|
|
* @param {Number} options.duration duration of the animation in ms, default 1000
|
|
* @param {bool} options.revers revers the animation direction
|
|
* @param {Number} options.repeat number of time to repeat the animation, default 0
|
|
* @param {oo.style.Style} options.hiddenStyle a style to display the feature when playing the animation
|
|
* to be used to make the feature selectable when playing animation
|
|
* (@see {@link ../examples/map.featureanimation.select.html}), default the feature
|
|
* will be hidden when playing (and niot selectable)
|
|
* @param {ol_easing_Function} options.fade an easing function used to fade in the feature, default none
|
|
* @param {ol_easing_Function} options.easing an easing function for the animation, default ol_easing.linear
|
|
*/
|
|
var ol_featureAnimation = function(options)
|
|
{ options = options || {};
|
|
|
|
this.duration_ = typeof (options.duration)=='number' ? (options.duration>=0 ? options.duration : 0) : 1000;
|
|
this.fade_ = typeof(options.fade) == 'function' ? options.fade : null;
|
|
this.repeat_ = Number(options.repeat);
|
|
|
|
var easing = typeof(options.easing) =='function' ? options.easing : ol_easing.linear;
|
|
if (options.revers) this.easing_ = function(t) { return (1 - easing(t)); };
|
|
else this.easing_ = easing;
|
|
|
|
this.hiddenStyle = options.hiddenStyle;
|
|
|
|
ol_Object.call(this);
|
|
};
|
|
ol.inherits(ol_featureAnimation, ol_Object);
|
|
|
|
/** Draw a geometry
|
|
* @param {olx.animateFeatureEvent} e
|
|
* @param {ol.geom} geom geometry for shadow
|
|
* @param {ol.geom} shadow geometry for shadow (ie. style with zIndex = -1)
|
|
* @private
|
|
*/
|
|
ol_featureAnimation.prototype.drawGeom_ = function (e, geom, shadow)
|
|
{ if (this.fade_)
|
|
{ e.context.globalAlpha = this.fade_(1-e.elapsed);
|
|
}
|
|
var style = e.style;
|
|
for (var i=0; i<style.length; i++)
|
|
{ var sc=0;
|
|
// OL < v4.3 : setImageStyle doesn't check retina
|
|
var imgs = ol_Map.prototype.getFeaturesAtPixel ? false : style[i].getImage();
|
|
if (imgs)
|
|
{ sc = imgs.getScale();
|
|
imgs.setScale(e.frameState.pixelRatio*sc);
|
|
}
|
|
// Prevent crach if the style is not ready (image not loaded)
|
|
try{
|
|
e.vectorContext.setStyle(style[i]);
|
|
if (style[i].getZIndex()<0) e.vectorContext.drawGeometry(shadow||geom);
|
|
else e.vectorContext.drawGeometry(geom);
|
|
} catch(e) {};
|
|
if (imgs) imgs.setScale(sc);
|
|
}
|
|
};
|
|
|
|
/** Function to perform manipulations onpostcompose.
|
|
* This function is called with an ol_featureAnimationEvent argument.
|
|
* The function will be overridden by the child implementation.
|
|
* Return true to keep this function for the next frame, false to remove it.
|
|
* @param {ol_featureAnimationEvent} e
|
|
* @return {bool} true to continue animation.
|
|
* @api
|
|
*/
|
|
ol_featureAnimation.prototype.animate = function (e)
|
|
{ return false;
|
|
};
|
|
|
|
/** An animation controler object an object to control animation with start, stop and isPlaying function.
|
|
* To be used with {@link olx.Map#animateFeature} or {@link ol.layer.Vector#animateFeature}
|
|
* @typedef {Object} ol.animationControler
|
|
* @property {function} start - start animation.
|
|
* @property {function} stop - stop animation option arguments can be passed in animationend event.
|
|
* @property {function} isPlaying - return true if animation is playing.
|
|
*/
|
|
|
|
/** Animate feature on a map
|
|
* @function
|
|
* @fires animationstart, animationend
|
|
* @param {ol.Feature} feature Feature to animate
|
|
* @param {ol_featureAnimation|Array<ol_featureAnimation>} fanim the animation to play
|
|
* @return {olx.animationControler} an object to control animation with start, stop and isPlaying function
|
|
*/
|
|
ol_Map.prototype.animateFeature =
|
|
|
|
/** Animate feature on a vector layer
|
|
* @fires animationstart, animationend
|
|
* @param {ol.Feature} feature Feature to animate
|
|
* @param {ol_featureAnimation|Array<ol_featureAnimation>} fanim the animation to play
|
|
* @return {olx.animationControler} an object to control animation with start, stop and isPlaying function
|
|
*/
|
|
ol_layer_Vector.prototype.animateFeature = function(feature, fanim)
|
|
{ var self = this;
|
|
var listenerKey;
|
|
|
|
// Save style
|
|
var style = feature.getStyle();
|
|
var flashStyle = style || (this.getStyleFunction ? this.getStyleFunction()(feature) : null);
|
|
if (!flashStyle) flashStyle=[];
|
|
if (!(flashStyle instanceof Array)) flashStyle = [flashStyle];
|
|
|
|
// Hide feature while animating
|
|
feature.setStyle(fanim.hiddenStyle || []);
|
|
|
|
// Structure pass for animating
|
|
var event =
|
|
{ // Frame context
|
|
vectorContext: null,
|
|
frameState: null,
|
|
start: 0,
|
|
time: 0,
|
|
elapsed: 0,
|
|
extent: false,
|
|
// Feature information
|
|
feature: feature,
|
|
geom: feature.getGeometry(),
|
|
typeGeom: feature.getGeometry().getType(),
|
|
bbox: feature.getGeometry().getExtent(),
|
|
coord: ol_extent.getCenter(feature.getGeometry().getExtent()),
|
|
style: flashStyle
|
|
};
|
|
|
|
if (!(fanim instanceof Array)) fanim = [fanim];
|
|
// Remove null animations
|
|
for (var i=fanim.length-1; i>=0; i--)
|
|
{ if (fanim[i].duration_===0) fanim.splice(i,1);
|
|
}
|
|
|
|
var nb=0, step = 0;
|
|
|
|
function animate(e)
|
|
{ event.vectorContext = e.vectorContext;
|
|
event.frameState = e.frameState;
|
|
if (!event.extent)
|
|
{ event.extent = e.frameState.extent;
|
|
event.start = e.frameState.time;
|
|
event.context = e.context;
|
|
}
|
|
event.time = e.frameState.time - event.start;
|
|
event.elapsed = event.time / fanim[step].duration_;
|
|
if (event.elapsed > 1) event.elapsed = 1;
|
|
|
|
// Stop animation?
|
|
if (!fanim[step].animate(event))
|
|
{ nb++;
|
|
// Repeat animation
|
|
if (nb < fanim[step].repeat_)
|
|
{ event.extent = false;
|
|
}
|
|
// newt step
|
|
else if (step < fanim.length-1)
|
|
{ fanim[step].dispatchEvent({ type:'animationend', feature: feature });
|
|
step++;
|
|
nb=0;
|
|
event.extent = false;
|
|
}
|
|
// the end
|
|
else
|
|
{ stop();
|
|
}
|
|
|
|
}
|
|
|
|
// tell OL3 to continue postcompose animation
|
|
e.frameState.animate = true;
|
|
}
|
|
|
|
// Stop animation
|
|
function stop(options)
|
|
{ ol_Observable.unByKey(listenerKey);
|
|
listenerKey = null;
|
|
feature.setStyle(style);
|
|
// Send event
|
|
var event = { type:'animationend', feature: feature };
|
|
if (options)
|
|
{ for (var i in options) if (options.hasOwnProperty(i))
|
|
{ event[i] = options[i];
|
|
}
|
|
}
|
|
fanim[step].dispatchEvent(event);
|
|
self.dispatchEvent(event);
|
|
}
|
|
|
|
// Launch animation
|
|
function start(options)
|
|
{ if (fanim.length && !listenerKey)
|
|
{ listenerKey = self.on('postcompose', animate, self);
|
|
// map or layer?
|
|
if (self.renderSync) self.renderSync();
|
|
else self.changed();
|
|
// Send event
|
|
var event = { type:'animationstart', feature: feature };
|
|
if (options)
|
|
{ for (var i in options) if (options.hasOwnProperty(i))
|
|
{ event[i] = options[i];
|
|
}
|
|
}
|
|
fanim[step].dispatchEvent(event);
|
|
self.dispatchEvent(event);
|
|
}
|
|
}
|
|
start();
|
|
|
|
// Return animation controler
|
|
return {
|
|
start: start,
|
|
stop: stop,
|
|
isPlaying: function() { return (!!listenerKey); }
|
|
};
|
|
};
|
|
|
|
export default ol_featureAnimation
|