diff --git a/packages/maptalks/src/ui/InfoWindow.ts b/packages/maptalks/src/ui/InfoWindow.ts index 96397ea391..6b6d2868e8 100644 --- a/packages/maptalks/src/ui/InfoWindow.ts +++ b/packages/maptalks/src/ui/InfoWindow.ts @@ -524,6 +524,8 @@ class InfoWindow extends UIComponent { return width; } + + } InfoWindow.mergeOptions(options); @@ -542,4 +544,5 @@ export type InfoWindowOptionsType = { content?: string | HTMLElement; enableTemplate?: boolean; + } & UIComponentOptionsType & UIComponentAlignOptionsType; diff --git a/packages/maptalks/src/ui/UIComponent.ts b/packages/maptalks/src/ui/UIComponent.ts index 2d9a829aef..2077687aba 100644 --- a/packages/maptalks/src/ui/UIComponent.ts +++ b/packages/maptalks/src/ui/UIComponent.ts @@ -18,7 +18,8 @@ import Coordinate from '../geo/Coordinate'; import type { Map } from './../map/Map'; import { Point } from '../geo'; import { MapStateCache } from '../map/MapStateCache'; - +import { bboxInBBOX, BBOX } from '../core/util/bbox'; +import { Marker } from '../geometry'; /** * @property {Object} options * @property {Boolean} [options.eventsPropagation=false] - whether stop ALL events' propagation. @@ -61,7 +62,8 @@ const options: UIComponentOptionsType = { 'collisionWeight': 0, 'collisionFadeIn': false, 'zIndex': 0, - 'enableScrollbar': true + 'enableScrollbar': true, + 'autoAdjustAnchor': false }; const COLLISION_STATES = ['collision', 'collisionBufferSize', 'collisionWeight', 'collisionFadeIn'] @@ -884,7 +886,7 @@ class UIComponent extends Eventable(Class) { } onMoving() { - if (this.isVisible() && this.getMap().isTransforming()) { + if (this.isVisible()) { this._updatePosition(); } } @@ -928,6 +930,8 @@ class UIComponent extends Eventable(Class) { return this; } + + //@internal _setPosition() { const dom = this.getDOM(); @@ -936,6 +940,7 @@ class UIComponent extends Eventable(Class) { const p = this.getPosition(); this._pos = p; dom.style[TRANSFORM] = this._toCSSTranslate(p) + ' scale(1)'; + this._autoAdjustAnchor(); } //@internal @@ -1073,6 +1078,87 @@ class UIComponent extends Eventable(Class) { this.fire('mouseout'); } + + _autoAdjustAnchor() { + const options = this.options as any; + if (!options.autoAdjustAnchor) { + return this + } + const dom = this.getDOM(); + const map = this.getMap(); + if (!map || !dom || !dom.getBoundingClientRect) { + return this + } + const rect = dom.getBoundingClientRect(); + const size = map.getSize(); + + const horizontalAlignment = options.horizontalAlignment; + const verticalAlignment = options.verticalAlignment; + + const width = size.width, height = size.height; + const x1 = rect.left, x2 = rect.right, y1 = rect.top, y2 = rect.bottom; + + let horizontalAlign = horizontalAlignment, + verticalAlign = verticalAlignment; + const w = rect.width, h = rect.height; + const halfw = w / 2; + + const bbox1 = [x1 - halfw, y1, x2 + halfw, y2], mapBBOX = [0, 0, width, height] as BBOX; + //always middle + if (bbox1[0] > 0 && bbox1[2] < width) { + horizontalAlign = 'middle'; + } + + const topJudge = () => { + if (verticalAlignment === 'bottom') { + const offset = { x: 0, y: 30 }; + const owner = this.getOwner() || {}; + if (owner instanceof Marker) { + const extent = owner._getFixedExtent(); + if (extent) { + const height = extent.getHeight(); + offset.y += height; + } + } + + const translateY = h + offset.y; + const bbox3 = [x1, y1 - translateY, x2, y2 - translateY]; + //判断是否可以 verticalAlign=top + if (bboxInBBOX(bbox3 as BBOX, mapBBOX)) { + verticalAlign = 'top'; + } + } + } + + //dom rect in map view + if (bboxInBBOX(bbox1 as BBOX, mapBBOX)) { + topJudge(); + } else { + if (x1 < 0) { + horizontalAlign = 'right'; + } + if (x2 > width) { + horizontalAlign = 'left'; + } + if (y1 < 0) { + verticalAlign = 'bottom'; + } + if (y2 > height) { + verticalAlign = 'top' + } + topJudge(); + } + + if (horizontalAlign === horizontalAlignment && verticalAlign === verticalAlignment) { + return this; + } + + this.config({ + horizontalAlignment: horizontalAlign, + verticalAlignment: verticalAlign + }) + + } } UIComponent.mergeOptions(options); @@ -1101,6 +1187,7 @@ export type UIComponentOptionsType = { zIndex?: number; cssName?: string | Array; enableScrollbar?: boolean; + autoAdjustAnchor?: boolean; } let resizeObserver: ResizeObserver; diff --git a/packages/maptalks/test/map/MapSpec.js b/packages/maptalks/test/map/MapSpec.js index dfc36f6de4..6f1caed89e 100644 --- a/packages/maptalks/test/map/MapSpec.js +++ b/packages/maptalks/test/map/MapSpec.js @@ -302,8 +302,13 @@ describe('Map.Spec', function () { it('zoom in/out with animation', function (done) { map.config('zoomAnimation', true); var cur = map.getZoom(); + function getZoom() { + const zoom = map.getZoom(); + return parseInt(zoom.toFixed(0)); + + } map.on('zoomend', function () { - expect(map.getZoom()).to.be.eql(cur + 1); + expect(getZoom()).to.be.eql(cur + 1); done(); }); map.zoomIn(); @@ -390,12 +395,12 @@ describe('Map.Spec', function () { }); it('fit to china extent', function (done) { - const chinaExtent = [73.499013,3.397894,135.087377,53.561308]; + const chinaExtent = [73.499013, 3.397894, 135.087377, 53.561308]; map.fitExtent(chinaExtent, 0, { 'animation': false }); const [lon, lat] = map.getCenter().toArray(); const lonDiff = Math.abs(lon - 104.293195); const latDiff = Math.abs(lat - 31.76872613); - const delta = Math.pow(10,-10); + const delta = Math.pow(10, -10); expect(lonDiff).to.be.below(delta); expect(latDiff).to.be.below(delta); done();