javascript 如何使用传单js在地图上精确放置div元素?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20790045/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 19:19:12  来源:igfitidea点击:

How to precisely place a div element on a map using leaflet js?

javascriptleaflet

提问by user3137607

I have tried this couple of ways but have not been able to get it working. I want to place clocks at the top of the map within multiple timezones. I have a the javascript to create the clock and I place the clock in a divelement.

我已经尝试了这几种方法,但一直无法让它工作。我想在多个时区的地图顶部放置时钟。我有一个 javascript 来创建时钟,并将时钟放在一个div元素中。

Here is what I tried:

这是我尝试过的:

  1. Create a Point with 0,0coordinates.
  2. From this point get the latitude value for the top of the map using containerPointToLatLng.
  3. Create LatLngusing the above lat and long for the timezone.
  4. Converted this LatLngto Point and then positioning the div element with the x,yfrom this point.
  1. 创建一个带0,0坐标的点。
  2. 从这一点开始,使用 获取地图顶部的纬度值containerPointToLatLng
  3. LatLng使用上述经纬度创建时区。
  4. 将其转换LatLng为 Point ,然后x,y从这一点定位 div 元素。

I execute the logic both when the page is first rendered and then on body resize. However, if I change the size of the browser window, the clock does not position correctly.

我在第一次呈现页面时执行逻辑,然后在调整正文大小时执行逻辑。但是,如果我更改浏览器窗口的大小,时钟将无法正确定位。

Any suggestions?

有什么建议?

回答by ghybs

To answer OP's precise issue, i.e. how to re-position the clock when the browser window is resized (hence map container dimensions may have changed), one should probably just re-compute the clock position on map's "resize"event.

要回答 OP 的确切问题,即如何在调整浏览器窗口大小时重新定位时钟(因此地图容器尺寸可能已更改),您可能应该重新计算地图"resize"事件上的时钟位置。

However, it is not clear whether OP placed the clock as a child of the map container, or somewhere else on the page DOM tree.

然而,目前尚不清楚 OP 是将时钟作为地图容器的子节点,还是页面 DOM 树上的其他位置。

It is probably much easier to place it as a child of the map, so that its position is always relative to the map container.

将其作为地图的子项放置可能要容易得多,因此其位置始终相对于地图容器。

What OP originally asked?

OP最初问的是什么?

If I understand correctly the original desired result, the OP would like to overlay a clock (or whatever information) on top of a particular geographical position (Toronto city in that case [UTC -5], according to comments).

如果我正确理解了最初的期望结果,OP 希望在特定地理位置(根据评论,在这种情况下为多伦多市 [UTC -5])之上覆盖一个时钟(或任何信息)。

However, the information container should not lay at a basemap fixed position, i.e. not at a precise geographic coordinated point (like a marker or a popup would), but at the top of the map container, similarly to a Control (hence iH8's original answer).

但是,信息容器不应位于底图固定位置,即不应位于精确的地理坐标点(如标记或弹出窗口),而应位于地图容器的顶部,类似于控件(因此 iH8 的原始答案)。

Except that it should not be totally fixed within the map container, but horizontally move with the city (or whatever specified geographical coordinates). Hence OP's comment to iH8's answer.

除了它不应该完全固定在地图容器内,而是随着城市(或任何指定的地理坐标)水平移动。因此,OP 对 iH8 的回答发表了评论。

Therefore it sounds like something similar to that site, except with an interactive (navigate-able) map and the "UTC-5" header replaced by a clock (or whatever information, hence an HTML container should do it) and horizontally following Toronto.

因此,它听起来类似于该站点,除了交互式(可导航)地图和由时钟(或任何信息,因此 HTML 容器应该这样做)替换的“UTC-5”标头并水平跟随多伦多。

Put differently, the clock should sit at a particular vertical line, i.e. longitude / meridian.

换句话说,时钟应该位于特定的垂直线上,即经度/子午线

Unfortunately, even 2 years and a half after the question is posted, there is still no Leaflet plugin that provides such functionality (at least within Leaflet plugins page).

不幸的是,即使在问题发布 2 年半之后,仍然没有提供此类功能的 Leaflet 插件(至少在Leaflet 插件页面中)。

Extending the use case to highly zoomed-in map…

将用例扩展到高度放大的地图……

That being said, and given the fact that the user may be able to zoom highly into the city (OP did not specify the maximum zoom level), it might not be a very good user experience having that clock horizontally follow a precise longitude: for example, it could track Toronto centroid / city hall / whatever particular place, and when user is zoomed-in at another city district, the clock is no longer visible, whereas he/she is still viewing a part of Toronto city…

话虽如此,而且鉴于用户可能能够高度放大城市(OP 没有指定最大缩放级别),让时钟水平跟随精确的经度可能不是一个很好的用户体验:对于例如,它可以跟踪多伦多质心/市政厅/任何特定的地方,当用户在另一个城区放大时,时钟不再可见,而他/她仍在观看多伦多市的一部分……

To extend that use case, the clock should very probably be visible in whatever area it applies, i.e. as soon as the map view port intersects the associated time zone.

为了扩展该用例,时钟应该很可能在它应用的任何区域都可见,即只要地图视图端口与相关的时区相交。

Extending the use case to highly zoomed-out map…

将用例扩展到高度缩小的地图……

Another point not detailed by OP, is what to do when places of different time zones are visible in the map view port? In the above mentioned site, we have one header per visible time zone, which seems the most complete information we can get.

还有一点OP没有详述,就是在地图视口中可以看到不同时区的地方怎么办?在上面提到的站点中,我们每个可见时区都有一个标题,这似乎是我们可以获得的最完整的信息。

But since Leaflet allows to zoom out down to level 0, where the entire world (i.e. essentially 24 time zones / actually 39 according to Wikipedia, not including potential effect of Daylight Saving Time - DST) is represented with a 256 pixels width, there is little room to fit all these clocks, if each one must be vertically aligned with its associated time zone.

但是由于 Leaflet 允许缩小到 0 级,其中整个世界(即基本上 24 个时区/根据维基百科实际上是 39 个,不包括夏令时 - DST 的潜在影响)以 256 像素宽度表示,有如果每个时钟都必须与其相关的时区垂直对齐,则几乎没有空间容纳所有这些时钟。

For now let's assume we do not care if clocks overlap.

现在让我们假设我们不关心时钟是否重叠。

Even more custom case…

更多定制案例……

But OP may have wished to display the clock only for particular places, not for the entire world. OP did not even say that clocks would be different (we could have clocks for cities in the same time zone, even though it could be more interesting to have these clocks sit next to their city - even on par with their latitude, so that it is easier to spot which city the clock is associated to, like in the case of 2 cities on the same meridian; but in that case, a marker with L.divIconwould be enough).

但是 OP 可能希望只显示特定地点的时钟,而不是整个世界。OP 甚至没有说时钟会有所不同(我们可以为同一时区的城市提供时钟,即使将这些时钟放在他们的城市旁边会更有趣 - 即使与他们的纬度相当,所以它更容易发现时钟与哪个城市相关联,就像在同一子午线上的 2 个城市的情况一样;但在这种情况下,一个标记L.divIcon就足够了)。

Hence a custom case would be not to consider official time zones, but developer's specified areas.

因此,自定义案例不会考虑官方时区,而是考虑开发人员的指定区域

So we forget about the latitude and try to align a clock vertically above the area, as long as it intersects the map view port.

所以我们忘记了纬度,并尝试在区域上方垂直对齐时钟,只要它与地图视口相交。

Describing a generic solution

描述通用解决方案

Therefore it sounds like a generic solution would be to enable the application developer to specify an array of HTML Elements, each one associated with a range of longitudes (could also be an area / polygon).

因此,听起来像一个通用的解决方案是让应用程序开发人员能够指定一个 HTML 元素数组,每个元素与一个经度范围(也可以是一个区域/多边形)相关联。

The time zones use case would then be a particular case where the specified areas are simply those from the time zones.

时区用例将是一个特殊情况,其中指定的区域只是来自时区的区域。

Then, each Element should be visible if and only if its associated area intersects the view port (therefore we introduce a possibility to hide it when the latitude range is out of view).

然后,当且仅当其关联区域与视口相交时,每个 Element 才应该可见(因此,我们引入了在纬度范围之外时隐藏它的可能性)。

As for positioning, let's choose:

至于定位,我们选择:

  • By the top of the map container (similar to a Control), as mentioned by OP.
  • Horizontally centered within the intersection of the view port and of the associated area.
  • 如OP所述,在地图容器的顶部(类似于控件)。
  • 在视口和相关区域的交点内水平居中。

HTML:

HTML:

<div id="map"></div>

<div id="clockToronto" class="clock leaflet-control">Clock here for Toronto</div>
<div id="clockBurlington" class="clock leaflet-control">Clock here for Burlington</div>

CSS:

CSS:

.clock {
  width: 150px;
  text-align: center;
  position: absolute;
  border: 1px solid black;
}

#clockToronto {
  background-color: yellow;
}

#clockBurlington {
  background-color: orange;
}

JavaScript:

JavaScript:

var map = L.map("map").setView([43.7, -79.4], 10);

// Application specific.
var clockTorontoElement = L.DomUtil.get("clockToronto"),
    clockBurlingtonElement = L.DomUtil.get("clockBurlington"),
    zones = [
      {
        element: clockTorontoElement, // Using the HTML Element for now.
        width: parseInt(window.getComputedStyle(clockTorontoElement, null).getPropertyValue("width"), 10),
        area: L.latLngBounds([43.58, -79.64], [43.86, -79.10]) // Using L.latLngBounds for now.
      },
      {
        element: clockBurlingtonElement, // Using the HTML Element for now.
        width: parseInt(window.getComputedStyle(clockBurlingtonElement, null).getPropertyValue("width"), 10),
        area: L.latLngBounds([43.28, -79.96], [43.48, -79.71]) // Using L.latLngBounds for now.
      }
    ];

// Initialization
var controlsContainer = map._container.getElementsByClassName("leaflet-control-container")[0],
    firstCorner = controlsContainer.firstChild,
    mapContainerWidth;

map.on("resize", setMapContainerWidth);
setMapContainerWidth();

// Applying the zones.
for (var i = 0; i < zones.length; i += 1) {
  setZone(zones[i]);
}

function setZone(zoneData) {
  // Visualize the area.
  L.rectangle(zoneData.area).addTo(map);
  console.log("width: " + zoneData.width);

  controlsContainer.insertBefore(zoneData.element, firstCorner);
  map.on("move resize", function () {
    updateZone(zoneData);
  });
  updateZone(zoneData);
}

function updateZone(zoneData) {
  var mapBounds = map.getBounds(),
      zoneArea = zoneData.area,
      style = zoneData.element.style;

  if (mapBounds.intersects(zoneArea)) {
    style.display = "block";

    var hcenterLng = getIntersectionHorizontalCenter(mapBounds, zoneArea),
        hcenter = isNaN(hcenterLng) ? 0 : map.latLngToContainerPoint([0, hcenterLng]).x;

    // Update Element position.
    // Could be refined to keep the entire Element visible, rather than cropping it.
    style.left = (hcenter - (zoneData.width / 2)) + "px";
  } else {
    style.display = "none";
  }
}

function getIntersectionHorizontalCenter(bounds1, bounds2) {
  var west1 = bounds1.getWest(),
      west2 = bounds2.getWest(),
      westIn = west1 < west2 ? west2 : west1,
      east1 = bounds1.getEast(),
      east2 = bounds2.getEast(),
      eastIn = east1 < east2 ? east1 : east2;

  return (westIn + eastIn) / 2;
}

function setMapContainerWidth() {
  mapContainerWidth = map.getSize().x;
}

enter image description here

在此处输入图片说明

Live demo: http://plnkr.co/edit/V2pvcva5S9OZ2N7LlI8r?p=preview

现场演示:http: //plnkr.co/edit/V2pvcva5S9OZ2N7LlI8r?p=preview

回答by iH8

Usually one would use L.Control to create a custom control which you can then add to the control layer. If you do so, leaflet will take care of positioning when resizing the map. Take a look at the reference for L.Control: http://leafletjs.com/reference.html#control

通常人们会使用 L.Control 创建一个自定义控件,然后您可以将其添加到控件层。如果这样做,传单将在调整地图大小时负责定位。看看 L.Control 的参考:http://leafletjs.com/reference.html#control

There is an example of a custom control in the reference: http://leafletjs.com/reference.html#icontrolIf you would like to see more examples you could check out one of the many custom control plugins to see how they implemented L.Control: http://leafletjs.com/plugins.html(under Controls and interaction)

参考中有一个自定义控件的示例:http: //leafletjs.com/reference.html#icontrol如果您想查看更多示例,您可以查看众多自定义控件插件之一,以了解它们是如何实现的.Control:http: //leafletjs.com/plugins.html(在控件和交互下)

The only drawback of L.Control is that you can't position a control vertically or horizontally centered. You may only use topleft, topright, bottomleft & bottomright.

L.Control 的唯一缺点是您不能垂直或水平居中放置控件。您只能使用左上、右上、左下和右下。