235 lines
6.9 KiB
JavaScript
235 lines
6.9 KiB
JavaScript
/* Copyright (c) 2020 Jean-Marc VIGLINO,
|
|
released under the CeCILL-B license (French BSD license)
|
|
(http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
|
|
*/
|
|
|
|
import ol_Overlay from 'ol/Overlay.js'
|
|
import {unByKey as ol_Observable_unByKey} from 'ol/Observable.js'
|
|
import ol_ext_element from '../util/element.js';
|
|
import ol_particule_Base from '../particule/Base.js';
|
|
|
|
/** An overlay to play animations on top of the map
|
|
* The overlay define a set of particules animated on top of the map.
|
|
* Particules are objects with coordinates.
|
|
* They are dawn in a canvas using the draw particule method.
|
|
* The update particule method updates the particule position according to the timelapse
|
|
*
|
|
* @constructor
|
|
* @extends {ol_Overlay}
|
|
* @param {*} options
|
|
* @param {String} options.className class of the Overlay
|
|
* @param {number} option.density particule density, default .5
|
|
* @param {number} option.speed particule speed, default 4
|
|
* @param {number} option.angle particule angle in radian, default PI/4
|
|
* @param {boolean} options.animate start animation, default true
|
|
* @param {number} options.fps frame per second, default 25
|
|
*/
|
|
var ol_Overlay_AnimatedCanvas = class olOverlayAnimatedCanvas extends ol_Overlay {
|
|
constructor(options) {
|
|
options = options || {};
|
|
|
|
var canvas = ol_ext_element.create('CANVAS', {
|
|
className: ((options.className || '') + ' ol-animated-overlay').trim()
|
|
});
|
|
super({
|
|
element: canvas,
|
|
stopEvent: false
|
|
});
|
|
|
|
this._canvas = canvas;
|
|
this._ctx = this._canvas.getContext('2d');
|
|
|
|
this._listener = [];
|
|
this._time = 0;
|
|
this._particuleClass = options.particule || ol_particule_Base;
|
|
if (options.createParticule)
|
|
this._createParticule = options.createParticule;
|
|
// 25fps
|
|
this._fps = 1000 / (options.fps || 25);
|
|
|
|
// Default particules properties
|
|
var p = this._createParticule();
|
|
this._psize = p.get('size') || [50, 50];
|
|
this.set('density', options.density || .5);
|
|
this.set('speed', options.speed || 4);
|
|
this.set('angle', typeof (options.angle) === 'number' ? options.angle : Math.PI / 4);
|
|
|
|
if (options.animate !== false)
|
|
this.setAnimation(true);
|
|
|
|
// Prevent animation when window on background
|
|
document.addEventListener("visibilitychange", function () {
|
|
this._pause = true;
|
|
}.bind(this));
|
|
}
|
|
/** Set the visibility
|
|
* @param {boolean} b
|
|
*/
|
|
setVisible(b) {
|
|
this.element.style.display = b ? 'block' : 'none';
|
|
if (b)
|
|
this.setAnimation(this.get('animation'));
|
|
}
|
|
/** Get the visibility
|
|
* @return {boolean} b
|
|
*/
|
|
getVisible() {
|
|
return this.element.style.display != 'none';
|
|
}
|
|
/** No update for this overlay
|
|
*/
|
|
updatePixelPosition() { }
|
|
/**
|
|
* Set the map instance the overlay is associated with
|
|
* @param {ol.Map} map The map instance.
|
|
*/
|
|
setMap(map) {
|
|
/*
|
|
if (this.getMap()) {
|
|
this.getMap().getViewport().querySelector('.ol-overlaycontainer').removeChild(this._canvas);
|
|
}
|
|
*/
|
|
this._listener.forEach(function (l) {
|
|
ol_Observable_unByKey(l);
|
|
});
|
|
this._listener = [];
|
|
|
|
super.setMap(map);
|
|
|
|
if (map) {
|
|
var size = map.getSize();
|
|
this._canvas.width = size[0];
|
|
this._canvas.height = size[1];
|
|
this.draw();
|
|
this._listener.push(map.on('change:size', function () {
|
|
var size = map.getSize();
|
|
if (this._canvas.width !== size[0] || this._canvas.height !== size[1]) {
|
|
this._canvas.width = size[0];
|
|
this._canvas.height = size[1];
|
|
this.draw();
|
|
}
|
|
}.bind(this)));
|
|
}
|
|
this.setAnimation(!!map);
|
|
}
|
|
/** Create particules or return exiting ones
|
|
*/
|
|
getParticules() {
|
|
var w = this._psize[0];
|
|
var h = this._psize[1];
|
|
var d = (this.get('density') * this._canvas.width * this._canvas.height / w / h) << 0;
|
|
if (!this._particules)
|
|
this._particules = [];
|
|
if (d > this._particules.length) {
|
|
for (var i = this._particules.length; i < d; i++) {
|
|
this._particules.push(this._createParticule(this, this.randomCoord()));
|
|
}
|
|
} else {
|
|
this._particules.length = d;
|
|
}
|
|
return this._particules;
|
|
}
|
|
/** Create a particule
|
|
* @private
|
|
*/
|
|
_createParticule(overlay, coordinate) {
|
|
return new this._particuleClass({
|
|
overlay: overlay,
|
|
coordinate: coordinate
|
|
});
|
|
}
|
|
/** Get random coordinates on canvas
|
|
*/
|
|
randomCoord() {
|
|
return [
|
|
Math.random() * (this._canvas.width + this._psize[0]) - this._psize[0] / 2,
|
|
Math.random() * (this._canvas.height + this._psize[1]) - this._psize[1] / 2
|
|
];
|
|
}
|
|
/** Draw canvas overlay (draw each particules)
|
|
* @param {number} dt timelapes since last call
|
|
*/
|
|
draw(dt) {
|
|
var ctx = this._ctx;
|
|
this.clear();
|
|
ctx.beginPath();
|
|
|
|
this.getParticules().forEach(function (p) {
|
|
if (dt) {
|
|
p.update(dt);
|
|
this.testExit(p);
|
|
}
|
|
p.draw(this._ctx);
|
|
}.bind(this));
|
|
}
|
|
/** Test if particule exit the canvas and add it on other side
|
|
* @param {*} p the point to test
|
|
* @param {ol.size} size size of the overlap
|
|
*/
|
|
testExit(p) {
|
|
var size = this._psize;
|
|
if (p.coordinate[0] < -size[0]) {
|
|
p.coordinate[0] = this._canvas.width + size[0];
|
|
p.coordinate[1] = Math.random() * (this._canvas.height + size[1]) - size[1] / 2;
|
|
} else if (p.coordinate[0] > this._canvas.width + size[0]) {
|
|
p.coordinate[0] = -size[0];
|
|
p.coordinate[1] = Math.random() * (this._canvas.height + size[1]) - size[1] / 2;
|
|
} else if (p.coordinate[1] < -size[1]) {
|
|
p.coordinate[0] = Math.random() * (this._canvas.width + size[0]) - size[0] / 2;
|
|
p.coordinate[1] = this._canvas.height + size[1];
|
|
} else if (p.coordinate[1] > this._canvas.height + size[1]) {
|
|
p.coordinate[0] = Math.random() * (this._canvas.width + size[0]) - size[0] / 2;
|
|
p.coordinate[1] = -size[1];
|
|
}
|
|
}
|
|
/** Clear canvas
|
|
*/
|
|
clear() {
|
|
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
|
}
|
|
/** Get overlay canvas
|
|
* @return {CanvasElement}
|
|
*/
|
|
getCanvas() {
|
|
return this._canvas;
|
|
}
|
|
/** Set canvas animation
|
|
* @param {boolean} anim, default true
|
|
* @api
|
|
*/
|
|
setAnimation(anim) {
|
|
anim = (anim !== false);
|
|
this.set('animation', anim);
|
|
if (anim) {
|
|
this._pause = true;
|
|
requestAnimationFrame(this._animate.bind(this));
|
|
} else {
|
|
this.dispatchEvent({ type: 'animation:stop', time: this._time });
|
|
}
|
|
}
|
|
/**
|
|
* @private
|
|
*/
|
|
_animate(time) {
|
|
if (this.getVisible() && this.get('animation')) {
|
|
if (this._pause) {
|
|
// reset time
|
|
requestAnimationFrame(function (time) {
|
|
this._time = time;
|
|
requestAnimationFrame(this._animate.bind(this));
|
|
}.bind(this));
|
|
} else {
|
|
// Test fps
|
|
if (time - this._time > this._fps) {
|
|
this.draw(time - this._time);
|
|
this._time = time;
|
|
}
|
|
requestAnimationFrame(this._animate.bind(this));
|
|
}
|
|
}
|
|
this._pause = false;
|
|
}
|
|
}
|
|
|
|
export default ol_Overlay_AnimatedCanvas
|