Avoid recomputing the viewport size by reading the DOM everytime

Also clarify View#calculateExtent doc & remove the [data-view] attribute
on the viewport element (not needed anymore).
This commit is contained in:
Olivier Guyot
2019-12-20 15:27:16 +01:00
parent c66a19c9cc
commit 4440994ec8
3 changed files with 75 additions and 33 deletions
+23 -2
View File
@@ -1,7 +1,6 @@
/** /**
* @module ol/PluggableMap * @module ol/PluggableMap
*/ */
import {getUid} from './util.js';
import Collection from './Collection.js'; import Collection from './Collection.js';
import CollectionEventType from './CollectionEventType.js'; import CollectionEventType from './CollectionEventType.js';
import MapBrowserEvent from './MapBrowserEvent.js'; import MapBrowserEvent from './MapBrowserEvent.js';
@@ -1102,7 +1101,8 @@ class PluggableMap extends BaseObject {
} }
const view = this.getView(); const view = this.getView();
if (view) { if (view) {
this.viewport_.setAttribute('data-view', getUid(view)); this.updateViewportSize_();
this.viewPropertyListenerKey_ = listen( this.viewPropertyListenerKey_ = listen(
view, ObjectEventType.PROPERTYCHANGE, view, ObjectEventType.PROPERTYCHANGE,
this.handleViewPropertyChanged_, this); this.handleViewPropertyChanged_, this);
@@ -1360,6 +1360,27 @@ class PluggableMap extends BaseObject {
parseFloat(computedStyle['borderBottomWidth']) parseFloat(computedStyle['borderBottomWidth'])
]); ]);
} }
this.updateViewportSize_();
}
/**
* Recomputes the viewport size and save it on the view object (if any)
* @private
*/
updateViewportSize_() {
const view = this.getView();
if (view) {
let size = undefined;
const computedStyle = getComputedStyle(this.viewport_);
if (computedStyle.width && computedStyle.height) {
size = [
parseInt(computedStyle.width, 10),
parseInt(computedStyle.height, 10)
];
}
view.setViewportSize(size);
}
} }
} }
+37 -25
View File
@@ -2,7 +2,6 @@
* @module ol/View * @module ol/View
*/ */
import {DEFAULT_TILE_SIZE} from './tilegrid/common.js'; import {DEFAULT_TILE_SIZE} from './tilegrid/common.js';
import {getUid} from './util.js';
import {VOID} from './functions.js'; import {VOID} from './functions.js';
import {createExtent, none as centerNone} from './centerconstraint.js'; import {createExtent, none as centerNone} from './centerconstraint.js';
import BaseObject from './Object.js'; import BaseObject from './Object.js';
@@ -294,6 +293,12 @@ class View extends BaseObject {
*/ */
this.projection_ = createProjection(options.projection, 'EPSG:3857'); this.projection_ = createProjection(options.projection, 'EPSG:3857');
/**
* @private
* @type {import("./size.js").Size}
*/
this.viewportSize_ = [100, 100];
/** /**
* @private * @private
* @type {import("./coordinate.js").Coordinate|undefined} * @type {import("./coordinate.js").Coordinate|undefined}
@@ -649,7 +654,7 @@ class View extends BaseObject {
animation.targetResolution : animation.targetResolution :
animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution); animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);
if (animation.anchor) { if (animation.anchor) {
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
const constrainedResolution = this.constraints_.resolution(resolution, 0, size, true); const constrainedResolution = this.constraints_.resolution(resolution, 0, size, true);
this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor); this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor);
} }
@@ -722,26 +727,33 @@ class View extends BaseObject {
} }
/** /**
* Returns the current viewport size.
* @private * @private
* @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size * @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size
* @return {import("./size.js").Size} Viewport size or `[100, 100]` when no viewport is found. * @return {import("./size.js").Size} Viewport size or `[100, 100]` when no viewport is found.
*/ */
getSizeFromViewport_(opt_rotation) { getViewportSize_(opt_rotation) {
const size = [100, 100]; const size = this.viewportSize_;
const selector = '.ol-viewport[data-view="' + getUid(this) + '"]';
const element = document.querySelector(selector);
if (element) {
const metrics = getComputedStyle(element);
size[0] = parseInt(metrics.width, 10);
size[1] = parseInt(metrics.height, 10);
}
if (opt_rotation) { if (opt_rotation) {
const w = size[0]; const w = size[0];
const h = size[1]; const h = size[1];
size[0] = Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation)); return [
size[1] = Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation)); Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation)),
Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation))
];
} else {
return size;
} }
return size; }
/**
* Stores the viewport size on the view. The viewport size is not read every time from the DOM
* to avoid performance hit and layout reflow.
* This should be done on map size change.
* @param {import("./size.js").Size=} opt_size Viewport size; if undefined, [100, 100] is assumed
*/
setViewportSize(opt_size) {
this.viewportSize_ = Array.isArray(opt_size) ? opt_size.slice() : [100, 100];
} }
/** /**
@@ -792,8 +804,8 @@ class View extends BaseObject {
* The size is the pixel dimensions of the box into which the calculated extent * The size is the pixel dimensions of the box into which the calculated extent
* should fit. In most cases you want to get the extent of the entire map, * should fit. In most cases you want to get the extent of the entire map,
* that is `map.getSize()`. * that is `map.getSize()`.
* @param {import("./size.js").Size=} opt_size Box pixel size. If not provided, the size of the * @param {import("./size.js").Size=} opt_size Box pixel size. If not provided, the size
* first map that uses this view will be used. * of the map that uses this view will be used.
* @return {import("./extent.js").Extent} Extent. * @return {import("./extent.js").Extent} Extent.
* @api * @api
*/ */
@@ -808,7 +820,7 @@ class View extends BaseObject {
* @return {import("./extent.js").Extent} Extent. * @return {import("./extent.js").Extent} Extent.
*/ */
calculateExtentInternal(opt_size) { calculateExtentInternal(opt_size) {
const size = opt_size || this.getSizeFromViewport_(); const size = opt_size || this.getViewportSize_();
const center = /** @type {!import("./coordinate.js").Coordinate} */ (this.getCenterInternal()); const center = /** @type {!import("./coordinate.js").Coordinate} */ (this.getCenterInternal());
assert(center, 1); // The view center is not defined assert(center, 1); // The view center is not defined
const resolution = /** @type {!number} */ (this.getResolution()); const resolution = /** @type {!number} */ (this.getResolution());
@@ -931,7 +943,7 @@ class View extends BaseObject {
* the given size. * the given size.
*/ */
getResolutionForExtentInternal(extent, opt_size) { getResolutionForExtentInternal(extent, opt_size) {
const size = opt_size || this.getSizeFromViewport_(); const size = opt_size || this.getViewportSize_();
const xResolution = getWidth(extent) / size[0]; const xResolution = getWidth(extent) / size[0];
const yResolution = getHeight(extent) / size[1]; const yResolution = getHeight(extent) / size[1];
return Math.max(xResolution, yResolution); return Math.max(xResolution, yResolution);
@@ -1079,7 +1091,7 @@ class View extends BaseObject {
* @api * @api
*/ */
fit(geometryOrExtent, opt_options) { fit(geometryOrExtent, opt_options) {
const options = assign({size: this.getSizeFromViewport_()}, opt_options || {}); const options = assign({size: this.getViewportSize_()}, opt_options || {});
/** @type {import("./geom/SimpleGeometry.js").default} */ /** @type {import("./geom/SimpleGeometry.js").default} */
let geometry; let geometry;
@@ -1114,7 +1126,7 @@ class View extends BaseObject {
const options = opt_options || {}; const options = opt_options || {};
let size = options.size; let size = options.size;
if (!size) { if (!size) {
size = this.getSizeFromViewport_(); size = this.getViewportSize_();
} }
const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0]; const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0];
const nearest = options.nearest !== undefined ? options.nearest : false; const nearest = options.nearest !== undefined ? options.nearest : false;
@@ -1261,7 +1273,7 @@ class View extends BaseObject {
*/ */
adjustResolutionInternal(ratio, opt_anchor) { adjustResolutionInternal(ratio, opt_anchor) {
const isMoving = this.getAnimating() || this.getInteracting(); const isMoving = this.getAnimating() || this.getInteracting();
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving); const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving);
if (opt_anchor) { if (opt_anchor) {
@@ -1385,7 +1397,7 @@ class View extends BaseObject {
// compute rotation // compute rotation
const newRotation = this.constraints_.rotation(this.targetRotation_, isMoving); const newRotation = this.constraints_.rotation(this.targetRotation_, isMoving);
const size = this.getSizeFromViewport_(newRotation); const size = this.getViewportSize_(newRotation);
const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving); const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving);
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving); const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving);
@@ -1419,7 +1431,7 @@ class View extends BaseObject {
const direction = opt_resolutionDirection || 0; const direction = opt_resolutionDirection || 0;
const newRotation = this.constraints_.rotation(this.targetRotation_); const newRotation = this.constraints_.rotation(this.targetRotation_);
const size = this.getSizeFromViewport_(newRotation); const size = this.getViewportSize_(newRotation);
const newResolution = this.constraints_.resolution(this.targetResolution_, direction, size); const newResolution = this.constraints_.resolution(this.targetResolution_, direction, size);
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size); const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size);
@@ -1500,7 +1512,7 @@ class View extends BaseObject {
* @return {import("./coordinate.js").Coordinate|undefined} Valid center position. * @return {import("./coordinate.js").Coordinate|undefined} Valid center position.
*/ */
getConstrainedCenter(targetCenter, opt_targetResolution) { getConstrainedCenter(targetCenter, opt_targetResolution) {
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
return this.constraints_.center(targetCenter, opt_targetResolution || this.getResolution(), size); return this.constraints_.center(targetCenter, opt_targetResolution || this.getResolution(), size);
} }
@@ -1529,7 +1541,7 @@ class View extends BaseObject {
*/ */
getConstrainedResolution(targetResolution, opt_direction) { getConstrainedResolution(targetResolution, opt_direction) {
const direction = opt_direction || 0; const direction = opt_direction || 0;
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
return this.constraints_.resolution(targetResolution, direction, size); return this.constraints_.resolution(targetResolution, direction, size);
} }
+15 -6
View File
@@ -1461,28 +1461,37 @@ describe('ol.View', function() {
}); });
}); });
describe('#getSizeFromViewport_()', function() { describe('#getViewportSize_()', function() {
let map, target; let map, target;
beforeEach(function() { beforeEach(function() {
target = document.createElement('div'); target = document.createElement('div');
target.style.width = '200px'; target.style.width = '200px';
target.style.height = '150px'; target.style.height = '150px';
document.body.appendChild(target);
map = new Map({ map = new Map({
target: target target: target
}); });
document.body.appendChild(target);
}); });
afterEach(function() { afterEach(function() {
map.setTarget(null); map.setTarget(null);
document.body.removeChild(target); document.body.removeChild(target);
}); });
it('calculates the size correctly', function() { it('correctly initializes the viewport size', function() {
let size = map.getView().getSizeFromViewport_(); const size = map.getView().getViewportSize_();
expect(size).to.eql([200, 150]); expect(size).to.eql([200, 150]);
size = map.getView().getSizeFromViewport_(Math.PI / 2); });
it('correctly updates the viewport size', function() {
target.style.width = '300px';
target.style.height = '200px';
map.updateSize();
const size = map.getView().getViewportSize_();
expect(size).to.eql([300, 200]);
});
it('calculates the size correctly', function() {
let size = map.getView().getViewportSize_(Math.PI / 2);
expect(size[0]).to.roughlyEqual(150, 1e-9); expect(size[0]).to.roughlyEqual(150, 1e-9);
expect(size[1]).to.roughlyEqual(200, 1e-9); expect(size[1]).to.roughlyEqual(200, 1e-9);
size = map.getView().getSizeFromViewport_(Math.PI); size = map.getView().getViewportSize_(Math.PI);
expect(size[0]).to.roughlyEqual(200, 1e-9); expect(size[0]).to.roughlyEqual(200, 1e-9);
expect(size[1]).to.roughlyEqual(150, 1e-9); expect(size[1]).to.roughlyEqual(150, 1e-9);
}); });