Popover overlay in OpenLayers 3 does not extend be

2019-07-18 00:46发布

In the OpenLayers overlay example:

http://openlayers.org/en/v3.11.2/examples/overlay.html

If you click near the top of map most of the overlay is hidden. Is there a CSS trick, or an OpenLayers setting (I do not want to use the autoPan, which doesn't seem to work for popovers anyway), that will enable the entire popover to be shown even if it extends beyond the map view?

Here's a screenshot that illustrates the problem.

enter image description here

4条回答
戒情不戒烟
2楼-- · 2019-07-18 00:49

autoPan does work for popups, see here: http://openlayers.org/en/v3.11.2/examples/popup.html

However, I also had some trouble with autoPan so I didi it like this (Fiddle demo):

// move map if popop sticks out of map area:
var extent = map.getView().calculateExtent(map.getSize());
var center = map.getView().getCenter();
var pixelPosition = map.getPixelFromCoordinate([ coordinate[0], coordinate[1] ]);
var mapWidth = $("#map").width();
var mapHeight = $("#map").height();
var popoverHeight = $("#popup").height();
var popoverWidth = $("#popup").width();
var thresholdTop = popoverHeight+50;
var thresholdBottom = mapHeight;
var thresholdLeft = popoverWidth/2-80;
var thresholdRight = mapWidth-popoverWidth/2-130;
if(pixelPosition[0] < thresholdLeft || pixelPosition[0] > thresholdRight || pixelPosition[1]<thresholdTop || pixelPosition[1]>thresholdBottom) {
    if(pixelPosition[0] < thresholdLeft) {
        var newX = pixelPosition[0]+(thresholdLeft-pixelPosition[0]);
    } else if(pixelPosition[0] > thresholdRight) {
        var newX = pixelPosition[0]-(pixelPosition[0]-thresholdRight);
    } else {
        var newX = pixelPosition[0];
    }
    if(pixelPosition[1]<thresholdTop) {
        var newY = pixelPosition[1]+(thresholdTop-pixelPosition[1]);
    } else if(pixelPosition[1]>thresholdBottom) {
        var newY = pixelPosition[1]-(pixelPosition[1]-thresholdBottom);
    } else {
        var newY = pixelPosition[1];
    }
    newCoordinate = map.getCoordinateFromPixel([newX, newY]);   
    newCenter = [(center[0]-(newCoordinate[0]-coordinate[0])), (center[1]-(newCoordinate[1]-coordinate[1])) ]
    map.getView().setCenter(newCenter);
}
查看更多
Fickle 薄情
3楼-- · 2019-07-18 01:05

I added this code to the Popover Official Example in this fiddle demo:

// get DOM element generated by Bootstrap
var bs_element = document.getElementById(element.getAttribute('aria-describedby'));
var offset_height = 10;
// get computed popup height and add some offset
var popup_height = bs_element.offsetHeight + offset_height;
var clicked_pixel = evt.pixel;
// how much space (height) left between clicked pixel and top
var height_left = clicked_pixel[1] - popup_height;
var view = map.getView();
// get the actual center
var center = view.getCenter();

if (height_left < 0) {
  var center_px = map.getPixelFromCoordinate(center);
  var new_center_px = [
    center_px[0],
    center_px[1] + height_left
  ];

  map.beforeRender(ol.animation.pan({
    source: center,
    start: Date.now(),
    duration: 300
  }));
  view.setCenter(map.getCoordinateFromPixel(new_center_px));
}
查看更多
The star\"
4楼-- · 2019-07-18 01:06

To make the popup always appear inside the map view, I reversed the ol3 autopan function So that it the popup is offset from the feature towards the view, instead of panning the view. I am not sure why so many ol3 fiddles are not loading the map anymore.

http://jsfiddle.net/bunjil/L6rztwj8/48/

  var getOverlayOffsets = function(mapInstance, overlay) {
  const overlayRect = overlay.getElement().getBoundingClientRect();
  const mapRect = mapInstance.getTargetElement().getBoundingClientRect();
  const margin = 15;
  // if (!ol.extent.containsExtent(mapRect, overlayRect)) //could use, but need to convert rect to extent
  const offsetLeft = overlayRect.left - mapRect.left;
  const offsetRight = mapRect.right - overlayRect.right;
  const offsetTop = overlayRect.top - mapRect.top;
  const offsetBottom = mapRect.bottom - overlayRect.bottom;
  console.log('offsets', offsetLeft, offsetRight, offsetTop, offsetBottom);

  const delta = [0, 0];
  if (offsetLeft < 0) {
    // move overlay to the right
    delta[0] = margin - offsetLeft;
  } else if (offsetRight < 0) {
    // move overlay  to the left
    delta[0] = -(Math.abs(offsetRight) + margin);
  }
  if (offsetTop < 0) {
    // will change the positioning instead of the offset to move overlay down.
    delta[1] = margin - offsetTop;
  } else if (offsetBottom < 0) {
    // move overlay up - never happens if bottome-center is default.
    delta[1] = -(Math.abs(offsetBottom) + margin);
  }
  return (delta);
};

/**
 * Add a click handler to the map to render the popup.
 */
map.on('click', function(evt) {
  var coordinate = evt.coordinate;
  var hdms = ol.coordinate.toStringHDMS(ol.proj.transform(
    coordinate, 'EPSG:3857', 'EPSG:4326'));

  content.innerHTML = '<p>You clicked here:</p><code>' + hdms +
    '</code>';

  //overlay.setPosition(coordinate);
  overlay.setOffset([0, 0]); // restore default
  overlay.setPositioning('bottom-right'); // restore default
  //overlay.set('autopan', true, false); //only need to do once.
  overlay.setPosition(coordinate);
  const delta = getOverlayOffsets(map, overlay);
  if (delta[1] > 0) {
    overlay.setPositioning('bottom-center');
  }
  overlay.setOffset(delta);
})

In this fiddle, the setPositioning() isn't working, so when you click near the top, the popup is under your mouse - it would be better to setPositioning('bottom-center');

automove would be a good feature to complement autopan.

查看更多
在下西门庆
5楼-- · 2019-07-18 01:14

In case of popover where "autoPan" option is not available you have to check extent's limits (top/bottom/right - left is skipped since popover is spawned on the center right of feature). So extending previous answer of Jonatas Walker a bit:

            var bs_element = $('.popover');
            var popup_height = bs_element.height();
            var popup_width = bs_element.width();
            var clicked_pixel = evt.pixel;
            var view = map.getView();
            var center = view.getCenter();

            var height_left = clicked_pixel[1] - popup_height / 2;  // from top
            var height_left2 = clicked_pixel[1] + popup_height / 2; // from bottom
            var width_left = clicked_pixel[0] + popup_width; // from right

            var center_px = map.getPixelFromCoordinate(center);
            var new_center_px = center_px;

            var needs_recenter = false;

            if (height_left2 > $("#map").height()) {
                new_center_px[1] = height_left2 - center_px[1] + 30;
                needs_recenter = true;
            }
            else if (height_left < 0) {
                new_center_px[1] = center_px[1] + height_left;
                needs_recenter = true;
            }

            if (width_left > $("#map").width()) {
                new_center_px[0] = width_left - center_px[0] + 30;
                needs_recenter = true;
            }

            if (needs_recenter)
                view.setCenter(map.getCoordinateFromPixel(new_center_px));
查看更多
登录 后发表回答