【问题标题】:SVG Zoom Issue Desktop vs Mobile with leaflet.jsSVG缩放问题桌面与移动与leaflet.js
【发布时间】:2022-01-25 16:43:08
【问题描述】:

我尝试使用leaflet.js 允许用户在我的网站上缩放和平移大型 SVG 文件。我使用以下script 来显示带有传单的 SVG:

    // Using leaflet.js to pan and zoom a big image.
    // See also: http://kempe.net/blog/2014/06/14/leaflet-pan-zoom-image.html
    var factor = 1;
    
    // create the slippy map
    var map = L.map('image-map', {
        minZoom: 1,
        maxZoom: 5,
        center: [0, 0],
        zoom: 1,
        crs: L.CRS.Simple
    });

    function getMeta(url) {
        const img = new Image();
        img.addEventListener("load", function () {
            var w = this.naturalWidth;
            var h = this.naturalHeight;
            var southWest = map.unproject([0, h], map.getMaxZoom() - 1);
            var northEast = map.unproject([w, 0], map.getMaxZoom() - 1);
            var bounds = new L.LatLngBounds(southWest, northEast);


            // add the image overlay, 
            // so that it covers the entire map
            L.imageOverlay(img.src, bounds).addTo(map);

            // tell leaflet that the map is exactly as big as the image
            map.setMaxBounds(bounds);
            map.fitBounds(bounds); // test

        });
        img.src = url;
    }

    getMeta("/assets/images/bhikkhu-patimokkha.svg")

您可以看到结果here。问题是它在我的 iPhone 上运行良好,缩放级别合适,很容易放大,但在桌面版本上,你只能缩小不能放大。

我尝试将 minZoom: 1 更改为不同的值,但它似乎没有做任何事情。我能够让它在桌面上工作的唯一方法是在 var wvar h 上添加一个乘数 10,但它会在移动版本上搞砸.

我在使用 PNG 图像方面取得了更多成功,但它们的加载速度非常慢。也许代码不适合 SVG,尤其是在调用 naturalHeight 时?但是当我调试它时它看起来很好。

感谢您的帮助。

如果你愿意,这里有一个codepen

编辑:使用来自@Grzegorz T 的更好的代码。它现在在桌面上运行良好。但在 Safari iOS 上,它被过度缩放并且无法取消缩放......见下图(它在 Iphone 上使用之前的代码,但在 Destop 上没有......)

【问题讨论】:

  • 尝试一个负数,例如minZoom: -4我知道很奇怪,但它对我有用 - image-instead-of-map
  • @Grzegorz T. 谢谢你,正如你提到的,我尝试了不同的值。到目前为止 min-zoom = 1 和 max-zoom = 0 给出了一个有趣的结果,但也不能放大。负最小缩放似乎不起作用。我现在已经制作了一个代码笔来解决我的问题。

标签: svg mobile leaflet zooming


【解决方案1】:

显示变量wh 你会看到返回了哪些小变量。为了增加它们,我将它们增加了* 5,为此我使用了 fitBounds,它现在应该缩放到查看器窗口,同时可以缩放。

为了能够多次单击缩放,我将map.getMaxZoom () - 1 更改为map.getMaxZoom () - 2

var map = L.map("image-map", {
  minZoom: 1, // tried 0.1,-4,...
  maxZoom: 4,
  center: [0, 0],
  zoom: 2,
  crs: L.CRS.Simple
});

function getMeta(url) {
  const img = new Image();
  img.addEventListener("load", function () {
    var w = this.naturalWidth * 5;
    var h = this.naturalHeight * 5;

    var southWest = map.unproject([0, h], map.getMaxZoom() - 2);
    var northEast = map.unproject([w, 0], map.getMaxZoom() - 2);
    var bounds = new L.LatLngBounds(southWest, northEast);

    // add the image overlay,
    // so that it covers the entire map
    L.imageOverlay(img.src, bounds).addTo(map);

    // tell leaflet that the map is exactly as big as the image
    // map.setMaxBounds(bounds);
    map.fitBounds(bounds);
  });
  img.src = url;
}

getMeta("https://fractalcitta.github.io/assets/images/bhikkhu-patimokkha.svg");

您无需增加(w, h) * 5,只需更改为map.getMaxZoom () - 4

还有一件更重要的事情,您应该始终使用 svg 来优化这些文件。 我总是使用这个网站 - svgomg

第二版宽度异步/承诺 ----------

let map = L.map("map", {
  crs: L.CRS.Simple,
  minZoom: 1,
  maxZoom: 4,
});

function loadImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.addEventListener("load", () => resolve(img));
    img.addEventListener("error", reject);
    img.src = url;
  });
}

async function getImageData(url) {
  const img = await loadImage(url);
  return { img, width: img.naturalWidth, height: img.naturalHeight };
}

async function showOnMap(url) {
  const { img, width, height } = await getImageData(url);

  const southWest = map.unproject([0, height], map.getMaxZoom() - 4);
  const northEast = map.unproject([width, 0], map.getMaxZoom() - 4);

  const bounds = new L.LatLngBounds(southWest, northEast);
  L.imageOverlay(img.src, bounds).addTo(map);

  map.fitBounds(bounds);
}

showOnMap(
  "https://fractalcitta.github.io/assets/images/bhikkhu-patimokkha.svg"
);

解决问题的第三种方法,希望是最后一种;)

您需要对这里发生的事情进行一些描述。 我们通过 fetch 获得 svg,我们将其注入隐藏的 div,其中 width/height 设置为 0
然后我们使用 getBBox() 属性从注入的 svg 中获取确切的尺寸。

在这个例子中我没有使用map.unproject。要获得确切的边界尺寸就足够了:

const bounds = [
  [0, 0], // padding
  [width, height], // image dimensions
];

以下所有代码:

<div id="map"></div>
<div id="svg" style="position: absolute; bottom: 0; left: 0; width: 0; height: 0;"></div>
let map = L.map("map", {
  crs: L.CRS.Simple,
  minZoom: -4,
  maxZoom: 1,
});

const url =
  "https://fractalcitta.github.io/assets/images/bhikkhu-patimokkha.svg";

async function fetchData(url) {
  try {
    const response = await fetch(url);
    const data = await response.text();
    return data;
  } catch (err) {
    console.error(err);
  }
}

fetchData(url)
  .then((svg) => {
    const map = document.getElementById("svg");
    map.insertAdjacentHTML("afterbegin", svg);
  })
  .then(() => {
    const svgElement = document.getElementById("svg");
    const { width, height } = svgElement.firstChild.getBBox();
    return { width, height };
  })
  .then(({ width, height }) => {
    const img = new Image();
    img.src = url;

    const bounds = [
      [0, 0], // padding
      [width, height], // image dimensions
    ];

    L.imageOverlay(img.src, bounds).addTo(map);

    map.fitBounds(bounds);
  });

【讨论】:

  • 好的,非常感谢。我已经使用您的第二个版本从我的 iphone 屏幕截图中编辑了我的问题。它被过度放大并且不能取消缩放。之前的代码在 iphone 上很好。但是您的代码在桌面上运行良好。也许我可以使用JS来检测移动设备并根据需要更改代码。
  • 不幸的是,我无法在 iPhone 上测试它,因为我没有它;) 在您的手机上调试页面。可以通过将Android 设备连接到 Windows。也许有类似的方法可以通过将 iPhone 连接到 Mac 来调试页面,但不幸的是,我不知道。
  • 再注意一点,自然宽度/高度在FF下不起作用,bugzilla.mozilla.org/show_bug.cgi?id=700533 这里描述svg问题github.com/whatwg/html/issues/3510#issuecomment-369982529这样最好如果可能的话,设定严格的界限。
  • 好的,我尝试连接我的 iphone 进行查看,它的高度 = 1354 比 Chrome 桌面 (= 150) 大得多。实际上它与 Safari Desktop 的行为相同。所以你是对的 naturalWidth/Height 根据浏览器给出不同的值。我会尝试一些事情。如果没有任何效果,我将为我加载的每个 svg 定义尺寸,但最好有一个函数在每个浏览器上一致地返回尺寸。
  • 好的,谢谢你的第三个方法。我已经尝试过并得到错误:“svgElement.getBBox 不是函数”。它在 getBBox() 之前需要 .firstChild,因为 svgElement 不是 svg,而是父 div#svg。我在这里解决了codepen.io/fractalcitta/pen/dyVZxpy。太棒了,它在移动设备和 Safari 上也运行良好。谢谢
【解决方案2】:

好的,所以最后我使用了来自@Grzegorz T. 的代码并稍作修改。添加一个 const H = 100;扮演任意高度的角色,然后使用浏览器给出的尺寸比计算宽度 W。 (比例相同,不同浏览器的SVG尺寸不同)

代码如下:

const W = 100; // this allow to get the correct ratio 
               // then it dosen't depend on browser giving different dimentions.

    let map = L.map("image-map", {
        crs: L.CRS.Simple,
        minZoom: 1,
        maxZoom: 4,
    });

    function loadImage(url) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.addEventListener("load", () => resolve(img));
            img.addEventListener("error", reject);
            img.src = url;
        });
    }

    async function getImageData(url) {
        const img = await loadImage(url);
        return { img, width: img.naturalWidth, height: img.naturalHeight };
    }

    async function showOnMap(url) {
        const { img, width, height } = await getImageData(url);

        const H = W * width / height; // hopefuly height is not = 0 (mozilla ?)

        const southWest = map.unproject([0, H], map.getMaxZoom() - 4);
        const northEast = map.unproject([W, 0], map.getMaxZoom() - 4);

        const bounds = new L.LatLngBounds(southWest, northEast);
        L.imageOverlay(img.src, bounds).addTo(map);

        map.fitBounds(bounds);
    }

    showOnMap(
        "https://fractalcitta.github.io/assets/images/bhikkhu-patimokkha.svg"
    );

【讨论】:

  • 我添加了第三个版本,可能是最终解决方案,在每个浏览器中都可以正确读取 svg 尺寸,可能除了 IE ;) 在 safari 上进行测试,但应该没问题。
猜你喜欢
  • 2018-07-09
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-03
  • 2012-02-09
相关资源
最近更新 更多