252 lines
7.9 KiB
JavaScript
252 lines
7.9 KiB
JavaScript
/* Copyright (c) 2019 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_Map from 'ol/Map.js'
|
|
import ol_ext_element from '../util/element.js';
|
|
import ol_Overlay from 'ol/Overlay.js'
|
|
import { inAndOut as ol_easing_inAndOut } from 'ol/easing.js'
|
|
import ol_matrix3D from '../util/matrix3D.js'
|
|
import { altKeyOnly as ol_events_condition_altKeyOnly } from 'ol/events/condition.js'
|
|
|
|
/** A map with a perspective
|
|
* @constructor
|
|
* @extends {ol.Map}
|
|
* @fires change:perspective
|
|
* @param {olx.MapOptions=} options
|
|
* @param {ol.events.condition} tiltCondition , default altKeyOnly
|
|
*/
|
|
var ol_PerspectiveMap = class olPerspectiveMap extends ol_Map {
|
|
constructor(options) {
|
|
|
|
// Map div
|
|
var divMap = options.target instanceof Element ? options.target : document.getElementById(options.target);
|
|
if (window.getComputedStyle(divMap).position !== 'absolute') {
|
|
divMap.style.position = 'relative';
|
|
}
|
|
divMap.style.overflow = 'hidden';
|
|
|
|
// Create map inside
|
|
var map = ol_ext_element.create('DIV', {
|
|
className: 'ol-perspective-map',
|
|
parent: divMap
|
|
});
|
|
var opts = {};
|
|
Object.assign(opts, options);
|
|
opts.target = map;
|
|
// enhance pixel ratio
|
|
//opts.pixelRatio = 2;
|
|
super(opts);
|
|
|
|
this._tiltCondition = options.tiltCondition || ol_events_condition_altKeyOnly;
|
|
|
|
}
|
|
/** Get pixel ratio for the map
|
|
*/
|
|
getPixelRatio() {
|
|
return window.devicePixelRatio;
|
|
}
|
|
/** Set perspective angle
|
|
* @param {number} angle the perspective angle 0 (vertical) - 30 (max), default 0
|
|
* @param {*} options
|
|
* @param {number} options.duration The duration of the animation in milliseconds, default 500
|
|
* @param {function} options.easing The easing function used during the animation, defaults to ol.easing.inAndOut).
|
|
*/
|
|
setPerspective(angle, options) {
|
|
options = options || {};
|
|
// max angle
|
|
if (angle > 30)
|
|
angle = 30;
|
|
else if (angle < 0)
|
|
angle = 0;
|
|
var fromAngle = this._angle || 0;
|
|
var toAngle = Math.round(angle * 10) / 10;
|
|
var style = this.getTarget().querySelector('.ol-layers').style;
|
|
cancelAnimationFrame(this._animatedPerspective);
|
|
requestAnimationFrame(function (t) {
|
|
this._animatePerpective(t, t, style, fromAngle, toAngle, options.duration, options.easing || ol_easing_inAndOut);
|
|
}.bind(this));
|
|
}
|
|
/** Animate the perspective
|
|
* @param {number} t0 starting timestamp
|
|
* @param {number} t current timestamp
|
|
* @param {CSSStyleDeclaration} style style to modify
|
|
* @param {number} fromAngle starting angle
|
|
* @param {number} toAngle ending angle
|
|
* @param {number} duration The duration of the animation in milliseconds, default 500
|
|
* @param {function} easing The easing function used during the animation, defaults to ol.easing.inAndOut).
|
|
* @private
|
|
*/
|
|
_animatePerpective(t0, t, style, fromAngle, toAngle, duration, easing) {
|
|
var dt, end;
|
|
if (duration === 0) {
|
|
dt = 1;
|
|
end = true;
|
|
} else {
|
|
dt = (t - t0) / (duration || 500);
|
|
end = (dt >= 1);
|
|
}
|
|
dt = easing(dt);
|
|
var angle;
|
|
if (end) {
|
|
angle = this._angle = toAngle;
|
|
} else {
|
|
angle = this._angle = fromAngle + (toAngle - fromAngle) * dt;
|
|
}
|
|
var fac = angle / 30;
|
|
// apply transform to the style
|
|
style.transform = 'translateY(-' + (17 * fac) + '%) perspective(200px) rotateX(' + angle + 'deg) scaleY(' + (1 - fac / 2) + ')';
|
|
this.getMatrix3D(true);
|
|
this.render();
|
|
if (!end) {
|
|
requestAnimationFrame(function (t) {
|
|
this._animatePerpective(t0, t, style, fromAngle, toAngle, duration || 500, easing || ol_easing_inAndOut);
|
|
}.bind(this));
|
|
}
|
|
// Dispatch event
|
|
this.dispatchEvent({
|
|
type: 'change:perspective',
|
|
angle: angle,
|
|
animating: !end
|
|
});
|
|
}
|
|
/** Convert to pixel coord according to the perspective
|
|
* @param {MapBrowserEvent} mapBrowserEvent The event to handle.
|
|
*/
|
|
handleMapBrowserEvent(e) {
|
|
e.pixel = [
|
|
e.originalEvent.offsetX / this.getPixelRatio(),
|
|
e.originalEvent.offsetY / this.getPixelRatio()
|
|
];
|
|
e.coordinate = this.getCoordinateFromPixel(e.pixel);
|
|
ol_Map.prototype.handleMapBrowserEvent.call(this, e);
|
|
|
|
// Change perspective on tilt condition
|
|
if (this._tiltCondition(e)) {
|
|
switch (e.type) {
|
|
case 'pointerdown': {
|
|
this._dragging = e.originalEvent.offsetY;
|
|
break;
|
|
}
|
|
case 'pointerup': {
|
|
this._dragging = false;
|
|
break;
|
|
}
|
|
case 'pointerdrag': {
|
|
if (this._dragging !== false) {
|
|
var angle = e.originalEvent.offsetY > this._dragging ? .5 : -.5;
|
|
if (angle) {
|
|
this.setPerspective((this._angle || 0) + angle, { duration: 0 });
|
|
}
|
|
this._dragging = e.originalEvent.offsetY;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
this._dragging = false;
|
|
}
|
|
|
|
}
|
|
/** Get map full teansform matrix3D
|
|
* @return {Array<Array<number>>}
|
|
*/
|
|
getMatrix3D(compute) {
|
|
if (compute) {
|
|
var ele = this.getTarget().querySelector('.ol-layers');
|
|
|
|
// Get transform matrix3D from CSS
|
|
var tx = ol_matrix3D.getTransform(ele);
|
|
|
|
// Get the CSS transform origin from the transformed parent - default is '50% 50%'
|
|
var txOrigin = ol_matrix3D.getTransformOrigin(ele);
|
|
|
|
// Compute the full transform that is applied to the transformed parent (-origin * tx * origin)
|
|
this._matrixTransform = ol_matrix3D.computeTransformMatrix(tx, txOrigin);
|
|
}
|
|
if (!this._matrixTransform)
|
|
this._matrixTransform = ol_matrix3D.identity();
|
|
return this._matrixTransform;
|
|
}
|
|
/** Get pixel at screen from coordinate.
|
|
* The default getPixelFromCoordinate get pixel in the perspective.
|
|
* @param {ol.coordinate} coord
|
|
* @param {ol.pixel}
|
|
*/
|
|
getPixelScreenFromCoordinate(coord) {
|
|
// Get pixel in the transform system
|
|
var px = this.getPixelFromCoordinate(coord);
|
|
|
|
// Get transform matrix3D from CSS
|
|
var fullTx = this.getMatrix3D();
|
|
|
|
// Transform the point using full transform
|
|
var pixel = ol_matrix3D.transformVertex(fullTx, px);
|
|
// Perform the homogeneous divide to apply perspective to the points (divide x,y,z by the w component).
|
|
pixel = ol_matrix3D.projectVertex(pixel);
|
|
|
|
return [pixel[0], pixel[1]];
|
|
}
|
|
/** Not working...
|
|
*
|
|
*/
|
|
getPixelFromPixelScreen(px) {
|
|
// Get transform matrix3D from CSS
|
|
var fullTx = ol_matrix3D.inverse(this.getMatrix3D());
|
|
|
|
// Transform the point using full transform
|
|
var pixel = ol_matrix3D.transformVertex(fullTx, px);
|
|
// Perform the homogeneous divide to apply perspective to the points (divide x,y,z by the w component).
|
|
pixel = ol_matrix3D.projectVertex(pixel);
|
|
|
|
return [pixel[0], pixel[1]];
|
|
}
|
|
}
|
|
|
|
/* HACK: Overwrited Overlay function to handle overlay positing in a perspective map */
|
|
;(function() {
|
|
var _updatePixelPosition = ol_Overlay.prototype.updatePixelPosition;
|
|
|
|
/** Update pixel projection in a perspective map (apply projection to the position)
|
|
* @private
|
|
*/
|
|
ol_Overlay.prototype.updatePixelPosition = function () {
|
|
var map = this.getMap();
|
|
if (map && map instanceof ol_PerspectiveMap) {
|
|
var position = this.getPosition();
|
|
if (!map || !map.isRendered() || !position) {
|
|
this.setVisible(false);
|
|
return;
|
|
}
|
|
// Get pixel at screen
|
|
|
|
var pixel = map.getPixelScreenFromCoordinate(position);
|
|
var mapSize = map.getSize();
|
|
pixel[0] -= mapSize[0]/4
|
|
pixel[1] -= mapSize[1]/4
|
|
/* for ol v6.2.x
|
|
// Offset according positioning
|
|
var pos = this.getPositioning();
|
|
if (/bottom/.test(pos)) {
|
|
pixel[1] += mapSize[1]/4
|
|
} else {
|
|
pixel[1] -= mapSize[1]/4
|
|
}
|
|
if (/right/.test(pos)) {
|
|
pixel[0] += mapSize[0]/4
|
|
} else {
|
|
pixel[0] -= mapSize[0]/4
|
|
}
|
|
*/
|
|
// Update
|
|
this.updateRenderedPosition(pixel , mapSize);
|
|
} else {
|
|
_updatePixelPosition.call(this);
|
|
}
|
|
};
|
|
/**/
|
|
|
|
})();
|
|
|
|
export default ol_PerspectiveMap
|