群里有个人问了个奇怪的问题,发现自己对resolution尚未理解透彻,先查看了一个例子,再查看源码
https://openlayers.org/en/latest/examples/interaction-options.html?
这个例子中有一句:By default, pinch-zoom and wheel/trackpad zoom interactions can leave the map at fractional zoom levels. If instead you want to constrain wheel/trackpad zooming to integer zoom levels, set constrainResolution: true;谷歌翻译
默认情况下,捏拉缩放和滚轮/触控板缩放交互可以使地图保持小数缩放级别。 如果您想要将轮/轨迹板缩放到整数缩放级别,请设置constrainResolution:true
保持小数级别?这个与自己平时对zoom的认识有些冲突了,我原以为zoom都是整数缩放的,跟踪一下源码
function handleEvent(mapBrowserEvent) {
if (!this.condition_(mapBrowserEvent)) {
return true;
}
const type = mapBrowserEvent.type;
if (type !== EventType.WHEEL && type !== EventType.MOUSEWHEEL) {
return true;
}
mapBrowserEvent.preventDefault();
const map = mapBrowserEvent.map;
const wheelEvent = /** @type {WheelEvent} */ (mapBrowserEvent.originalEvent);
if (this.useAnchor_) {
this.lastAnchor_ = mapBrowserEvent.coordinate;
}
// Delta normalisation inspired by
// https://github.com/mapbox/mapbox-gl-js/blob/001c7b9/js/ui/handler/scroll_zoom.js
let delta;
if (mapBrowserEvent.type == EventType.WHEEL) {
delta = wheelEvent.deltaY;
if (FIREFOX &&
wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
delta /= DEVICE_PIXEL_RATIO;
}
if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) {
delta *= 40;
}
} else if (mapBrowserEvent.type == EventType.MOUSEWHEEL) {
delta = -wheelEvent.wheelDeltaY;
if (SAFARI) {
delta /= 3;
}
}
.
.
.
}
这个函数首先计算了滚轮的滚动距离,函数的最后实际处理了鼠标滚动事件,多点触控缩放另说。
this.timeoutId_ = setTimeout(this.handleWheelZoom_.bind(this, map), timeLeft);
handleWheelZoom_(map) {
const view = map.getView();
if (view.getAnimating()) {
view.cancelAnimations();
}
const maxDelta = MAX_DELTA;
const delta = clamp(this.delta_, -maxDelta, maxDelta);
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
this.mode_ = undefined;
this.delta_ = 0;
this.lastAnchor_ = null;
this.startTime_ = undefined;
this.timeoutId_ = undefined;
}
按我的理解这个clamp函数将距离转换为了zoom的变化数,要么是-1,要么是1,
而zoomByDetla中利用View.js 中的,计算了在resolutions未设置或已设置的情况下,resolution的选择。
export function zoomByDelta(view, delta, opt_anchor, opt_duration) {
const currentResolution = view.getResolution();
let resolution = view.constrainResolution(currentResolution, delta, 0);
if (resolution !== undefined) {
const resolutions = view.getResolutions();
resolution = clamp(
resolution,
view.getMinResolution() || resolutions[resolutions.length - 1],
view.getMaxResolution() || resolutions[0]);
}
// If we have a constraint on center, we need to change the anchor so that the
// new center is within the extent. We first calculate the new center, apply
// the constraint to it, and then calculate back the anchor
if (opt_anchor && resolution !== undefined && resolution !== currentResolution) {
const currentCenter = view.getCenter();
let center = view.calculateCenterZoom(resolution, opt_anchor);
center = view.constrainCenter(center);
opt_anchor = [
(resolution * currentCenter[0] - currentResolution * center[0]) /
(resolution - currentResolution),
(resolution * currentCenter[1] - currentResolution * center[1]) /
(resolution - currentResolution)
];
}
zoomWithoutConstraints(view, resolution, opt_anchor, opt_duration);
}
这个函数计算了缩放后的分辨率。
view.constrainResolution(currentResolution, delta, 0);
这两个函数分别计算了有无设置resolutions时缩放的分辨率,
有则:createSnapToResolutions(resolutions); 无则:createSnapToPower(zoomFactor, maxResolution, maxZoom - minZoom);
export function createSnapToPower(power, maxResolution, opt_maxLevel) {
return (
/**
* @param {number|undefined} resolution Resolution.
* @param {number} delta Delta.
* @param {number} direction Direction.
* @return {number|undefined} Resolution.
*/
function(resolution, delta, direction) {
if (resolution !== undefined) {
const offset = -direction / 2 + 0.5;
const oldLevel = Math.floor(
Math.log(maxResolution / resolution) / Math.log(power) + offset);
let newLevel = Math.max(oldLevel + delta, 0);
if (opt_maxLevel !== undefined) {
newLevel = Math.min(newLevel, opt_maxLevel);
}
return maxResolution / Math.pow(power, newLevel);
} else {
return undefined;
}
});
}
可以看到,首先根据最大层级(固定常量28目前ol的设定),及缩放前的分辨率计算出缩放前的旧分辨率,然后增加一层,根据增加的一层重新计算了分辨率,感觉这样很绕,不知为何。
而若已经设定了resolutions数组,则吸附到最近的一个分辨率上,
export function createSnapToResolutions(resolutions) {
return (
/**
* @param {number|undefined} resolution Resolution.
* @param {number} delta Delta.
* @param {number} direction Direction.
* @return {number|undefined} Resolution.
*/
function(resolution, delta, direction) {
if (resolution !== undefined) {
let z = linearFindNearest(resolutions, resolution, direction);
z = clamp(z + delta, 0, resolutions.length - 1);
const index = Math.floor(z);
if (z != index && index < resolutions.length - 1) {
const power = resolutions[index] / resolutions[index + 1];
return resolutions[index] / Math.pow(power, z - index);
} else {
return resolutions[index];
}
} else {
return undefined;
}
}
);
}
所以,根据源码查看,在缩放过程中,不会出现对应一个zoom带小数的对应的分辨率的,不知道文章开始时的例子为何说会存在保持缩放小数级别的?不解!
而view若未设定强制指定缩放到整数级,fit函数实际上是可以重新计算resolution的
// calculate resolution
let resolution = this.getResolutionForExtent(
[minRotX, minRotY, maxRotX, maxRotY],
[size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);
resolution = isNaN(resolution) ? minResolution :
Math.max(resolution, minResolution);
if (constrainResolution) {
let constrainedResolution = this.constrainResolution(resolution, 0, 0);
if (!nearest && constrainedResolution < resolution) {
constrainedResolution = this.constrainResolution(
constrainedResolution, -1, 0);
}
resolution = constrainedResolution;
}
分辨率计算,
getResolutionForExtent(extent, opt_size) {
const size = opt_size || this.getSizeFromViewport_();
const xResolution = getWidth(extent) / size[0];
const yResolution = getHeight(extent) / size[1];
return Math.max(xResolution, yResolution);
}