View / add adjust* methods to manipulate the view more easily

API changes:
* (breaking) the `rotate` method is gone
* the `adjustRotation`, `adjustResolution` and `adjustZoom` methods are now
  available and allow using an anchor.

This means interactions do not have to do the anchor computation themselves
and this also fix anchor computation when constraints must be applied.
This commit is contained in:
Olivier Guyot
2019-01-29 12:13:27 +01:00
parent b5273babb5
commit e023c144bb
10 changed files with 237 additions and 284 deletions

View File

@@ -635,10 +635,10 @@ class View extends BaseObject {
*/
calculateCenterRotate(rotation, anchor) {
let center;
const currentCenter = this.targetCenter_;
const currentCenter = this.getCenter();
if (currentCenter !== undefined) {
center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]];
rotateCoordinate(center, rotation - this.targetRotation_);
rotateCoordinate(center, rotation - this.getRotation());
addCoordinate(center, anchor);
}
return center;
@@ -651,8 +651,8 @@ class View extends BaseObject {
*/
calculateCenterZoom(resolution, anchor) {
let center;
const currentCenter = this.targetCenter_;
const currentResolution = this.targetResolution_;
const currentCenter = this.getCenter();
const currentResolution = this.getResolution();
if (currentCenter !== undefined && currentResolution !== undefined) {
const x = anchor[0] - resolution * (anchor[0] - currentCenter[0]) / currentResolution;
const y = anchor[1] - resolution * (anchor[1] - currentCenter[1]) / currentResolution;
@@ -1123,17 +1123,50 @@ class View extends BaseObject {
}
/**
* Rotate the view around a given coordinate.
* @param {number} rotation New rotation value for the view.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The rotation center.
* Multiply the view resolution by a ratio, optionally using an anchor.
* @param {number} ratio The ratio to apply on the view resolution.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation.
* @observable
* @api
*/
rotate(rotation, opt_anchor) {
adjustResolution(ratio, opt_anchor) {
const isMoving = this.getAnimating() || this.getInteracting();
const size = this.getSizeFromViewport_(this.getRotation());
const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving);
if (opt_anchor !== undefined) {
const center = this.calculateCenterRotate(rotation, opt_anchor);
this.setCenter(center);
this.targetCenter_ = this.calculateCenterZoom(newResolution, opt_anchor);
}
this.setRotation(rotation);
this.targetResolution_ *= ratio;
this.applyParameters_();
}
/**
* Adds a value to the view zoom level, optionally using an anchor.
* @param {number} delta Relative value to add to the zoom level.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation.
* @api
*/
adjustZoom(delta, opt_anchor) {
this.adjustResolution(Math.pow(this.zoomFactor_, -delta), opt_anchor);
}
/**
* Adds a value to the view rotation, optionally using an anchor.
* @param {number} delta Relative value to add to the zoom rotation, in radians.
* @param {import("./coordinate.js").Coordinate=} opt_anchor The rotation center.
* @observable
* @api
*/
adjustRotation(delta, opt_anchor) {
const isMoving = this.getAnimating() || this.getInteracting();
const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving);
if (opt_anchor !== undefined) {
this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor);
}
this.targetRotation_ += delta;
this.applyParameters_();
}
/**
@@ -1206,9 +1239,15 @@ class View extends BaseObject {
const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving);
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving);
this.set(ViewProperty.ROTATION, newRotation);
this.set(ViewProperty.RESOLUTION, newResolution);
this.set(ViewProperty.CENTER, newCenter);
if (this.get(ViewProperty.ROTATION) !== newRotation) {
this.set(ViewProperty.ROTATION, newRotation);
}
if (this.get(ViewProperty.RESOLUTION) !== newResolution) {
this.set(ViewProperty.RESOLUTION, newResolution);
}
if (!this.get(ViewProperty.CENTER) || !equals(this.get(ViewProperty.CENTER), newCenter)) {
this.set(ViewProperty.CENTER, newCenter);
}
if (this.getAnimating() && !opt_doNotCancelAnims) {
this.cancelAnimations();

View File

@@ -80,8 +80,7 @@ class DragRotate extends PointerInteraction {
Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2);
if (this.lastAngle_ !== undefined) {
const delta = theta - this.lastAngle_;
const rotation = view.getRotation();
rotate(view, rotation - delta);
view.adjustRotation(-delta);
}
this.lastAngle_ = theta;
}

View File

@@ -4,7 +4,6 @@
import {disable} from '../rotationconstraint.js';
import ViewHint from '../ViewHint.js';
import {shiftKeyOnly, mouseOnly} from '../events/condition.js';
import {rotate, zoom} from './Interaction.js';
import PointerInteraction from './Pointer.js';
@@ -88,14 +87,13 @@ class DragRotateAndZoom extends PointerInteraction {
const theta = Math.atan2(deltaY, deltaX);
const magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
const view = map.getView();
if (view.getConstraints().rotation !== disable && this.lastAngle_ !== undefined) {
const angleDelta = theta - this.lastAngle_;
rotate(view, view.getRotation() - angleDelta);
if (this.lastAngle_ !== undefined) {
const angleDelta = this.lastAngle_ - theta;
view.adjustRotation(angleDelta);
}
this.lastAngle_ = theta;
if (this.lastMagnitude_ !== undefined) {
const resolution = this.lastMagnitude_ * (view.getResolution() / magnitude);
zoom(view, resolution);
view.adjustResolution(this.lastMagnitude_ / magnitude);
}
if (this.lastMagnitude_ !== undefined) {
this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;

View File

@@ -111,80 +111,14 @@ export function pan(view, delta, opt_duration) {
const currentCenter = view.getCenter();
if (currentCenter) {
const center = [currentCenter[0] + delta[0], currentCenter[1] + delta[1]];
if (opt_duration) {
view.animate({
duration: opt_duration,
easing: linear,
center: center
});
} else {
view.setCenter(center);
}
view.animate({
duration: opt_duration !== undefined ? opt_duration : 250,
easing: linear,
center: center
});
}
}
/**
* @param {import("../View.js").default} view View.
* @param {number|undefined} rotation Rotation.
* @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate.
* @param {number=} opt_duration Duration.
*/
export function rotate(view, rotation, opt_anchor, opt_duration) {
if (rotation !== undefined) {
const currentRotation = view.getRotation();
const currentCenter = view.getCenter();
if (currentRotation !== undefined && currentCenter && opt_duration > 0) {
view.animate({
rotation: rotation,
anchor: opt_anchor,
duration: opt_duration,
easing: easeOut
});
} else {
view.rotate(rotation, opt_anchor);
}
}
}
/**
* @param {import("../View.js").default} view View.
* @param {number|undefined} resolution Resolution to go to.
* @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate.
* @param {number=} opt_duration Duration.
* @param {number=} opt_direction Zooming direction; > 0 indicates
* zooming out, in which case the constraints system will select
* the largest nearest resolution; < 0 indicates zooming in, in
* which case the constraints system will select the smallest
* nearest resolution; == 0 indicates that the zooming direction
* is unknown/not relevant, in which case the constraints system
* will select the nearest resolution. If not defined 0 is
* assumed.
*/
export function zoom(view, resolution, opt_anchor, opt_duration, opt_direction) {
if (resolution) {
const currentResolution = view.getResolution();
const currentCenter = view.getCenter();
if (currentResolution !== undefined && currentCenter &&
resolution !== currentResolution && opt_duration) {
view.animate({
resolution: resolution,
anchor: opt_anchor,
duration: opt_duration,
easing: easeOut
});
} else {
if (opt_anchor) {
const center = view.calculateCenterZoom(resolution, opt_anchor);
view.setCenter(center);
}
view.setResolution(resolution);
}
}
}
/**
* @param {import("../View.js").default} view View.
* @param {number} delta Delta from previous zoom level.
@@ -201,23 +135,15 @@ export function zoomByDelta(view, delta, opt_anchor, opt_duration) {
const newZoom = view.getValidZoomLevel(currentZoom + delta);
const newResolution = view.getResolutionForZoom(newZoom);
if (opt_duration > 0) {
if (view.getAnimating()) {
view.cancelAnimations();
}
view.animate({
resolution: newResolution,
anchor: opt_anchor,
duration: opt_duration,
easing: easeOut
});
} else {
if (opt_anchor) {
const center = view.calculateCenterZoom(newResolution, opt_anchor);
view.setCenter(center);
}
view.setResolution(newResolution);
if (view.getAnimating()) {
view.cancelAnimations();
}
view.animate({
resolution: newResolution,
anchor: opt_anchor,
duration: opt_duration !== undefined ? opt_duration : 250,
easing: easeOut
});
}
export default Interaction;

View File

@@ -212,49 +212,7 @@ class MouseWheelZoom extends Interaction {
view.beginInteraction();
}
this.trackpadTimeoutId_ = setTimeout(this.decrementInteractingHint_.bind(this), this.trackpadEventGap_);
let resolution = view.getResolution() * Math.pow(2, delta / this.trackpadDeltaPerZoom_);
const minResolution = view.getMinResolution();
const maxResolution = view.getMaxResolution();
let rebound = 0;
if (resolution < minResolution) {
resolution = Math.max(resolution, minResolution / this.trackpadZoomBuffer_);
rebound = 1;
} else if (resolution > maxResolution) {
resolution = Math.min(resolution, maxResolution * this.trackpadZoomBuffer_);
rebound = -1;
}
if (this.lastAnchor_) {
const center = view.calculateCenterZoom(resolution, this.lastAnchor_);
view.setCenter(center);
}
view.setResolution(resolution);
if (rebound === 0) {
const zoomDelta = delta > 0 ? -1 : 1;
const newZoom = view.getValidZoomLevel(view.getZoom() + zoomDelta);
view.animate({
resolution: view.getResolutionForZoom(newZoom),
easing: easeOut,
anchor: this.lastAnchor_,
duration: this.duration_
});
}
if (rebound > 0) {
view.animate({
resolution: minResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
} else if (rebound < 0) {
view.animate({
resolution: maxResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
}
view.adjustZoom(-delta / this.trackpadDeltaPerZoom_, this.lastAnchor_);
this.startTime_ = now;
return false;
}

View File

@@ -118,9 +118,8 @@ class PinchRotate extends PointerInteraction {
// rotate
if (this.rotating_) {
const rotation = view.getRotation();
map.render();
rotate(view, rotation + rotationDelta, this.anchor_);
view.adjustRotation(rotationDelta, this.anchor_);
}
}

View File

@@ -83,17 +83,6 @@ class PinchZoom extends PointerInteraction {
const map = mapBrowserEvent.map;
const view = map.getView();
const resolution = view.getResolution();
const maxResolution = view.getMaxResolution();
const minResolution = view.getMinResolution();
let newResolution = resolution * scaleDelta;
if (newResolution > maxResolution) {
scaleDelta = maxResolution / resolution;
newResolution = maxResolution;
} else if (newResolution < minResolution) {
scaleDelta = minResolution / resolution;
newResolution = minResolution;
}
if (scaleDelta != 1.0) {
this.lastScaleDelta_ = scaleDelta;
@@ -108,7 +97,7 @@ class PinchZoom extends PointerInteraction {
// scale, bypass the resolution constraint
map.render();
zoom(view, newResolution, this.anchor_);
view.adjustResolution(scaleDelta, this.anchor_);
}
/**
@@ -118,7 +107,7 @@ class PinchZoom extends PointerInteraction {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
const direction = this.lastScaleDelta_ - 1;
const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
view.endInteraction(this.duration_, direction);
return false;
} else {