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

时间:2013-12-26 19:19:16

标签: javascript leaflet

我尝试了这几种方法,但未能使其正常工作。我想在多个时区内将时钟放在地图的顶部。我有一个用于创建时钟的javascript,我将时钟放在div元素中。

以下是我的尝试:

  1. 使用0,0坐标创建一个点。
  2. 从此点开始,使用containerPointToLatLng获取地图顶部的纬度值。
  3. 使用上面的lat创建LatLng,为时区创建long。
  4. 将此LatLng转换为Point,然后从此处将div元素与x,y对齐。
  5. 我首先渲染页面然后在body调整大小时执行逻辑。但是,如果我更改浏览器窗口的大小,则时钟位置不正确。

    有什么建议吗?

2 个答案:

答案 0 :(得分:13)

要回答OP的精确问题,即如何在调整浏览器窗口大小时重新定位时钟(因此地图容器尺寸可能已经改变),可能只需重新计算地图上的时钟位置&# 39; s "resize"事件。

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

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

OP最初问的是什么?

如果我理解正确的原始期望结果,OP希望在特定地理位置(根据评论的情况下,多伦多市[UTC -5])覆盖时钟(或任何信息)。< / p>

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

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

因此它听起来类似于that site,除了交互式(可导航)地图和&#34; UTC-5&#34;标题被时钟(或任何信息,因此HTML容器应该这样做)替换为水平跟随多伦多。

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

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

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

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

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

将用例扩展为高度缩小的地图......

OP未详细说明的另一点是,在地图视口中可以看到不同时区的地点时该怎么办?在above mentioned site中,我们每个可见时区一个标题,这似乎是我们可以获得的最完整的信息。

但是由于Leaflet允许缩小到0级,整个世界(即基本上24个时区/ actually 39 according to Wikipedia,不包括夏令时的潜在影响 - DST)用256像素宽度表示如果每个时钟必须与其相关的时区垂直对齐,那么几乎没有空间适合所有这些时钟。

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

更多定制案例......

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

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

所以我们忘记了纬度,并尝试将一个时钟垂直对准该区域,只要它与地图视口相交。

描述通用解决方案

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

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

然后,当且仅当其相关区域与视口相交时,每个元素都应该是可见的(因此,当纬度范围不在视图范围内时,我们会引入隐藏它的可能性。)

至于定位,让我们选择:

  • 在地图容器的顶部(类似于Control),如OP。
  • 所述
  • 在视口和相关区域的交叉点内水平居中。

<强> 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:

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

#clockToronto {
  background-color: yellow;
}

#clockBurlington {
  background-color: orange;
}

<强> 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

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

答案 1 :(得分:2)

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

在引用中有一个自定义控件的示例:http://leafletjs.com/reference.html#icontrol如果您想查看更多示例,可以查看其中一个自定义控件插件,看看它们是如何实现的.Control:{{ 3}}(在控制和互动下)

L.Control的唯一缺点是您无法垂直或水平居中定位控件。您只能使用topleft,topright,bottomleft&amp; bottomright。