SVG 缩放问题桌面与移动 leaflet.js

SVG Zoom Issue Desktop vs Mobile with leaflet.js

我尝试使用 leaflet.js to allow user to zoom and pan on big SVG files on my website. I've used the following 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 更改为不同的值,但它似乎没有任何作用。我能够让它在桌面上工作的唯一方法是将乘数 10 添加到 var wvar h,但它会搞砸移动版本。

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

感谢您的帮助。

这里有一个 codepen 如果你想玩的话。

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

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

为了能够多次单击缩放,我将 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

第二版宽度async/promise--------

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"
);

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

你需要对这里发生的事情做一些简单的描述。 我们通过 featch 获得 svg,我们将其注入 hidden 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);
  });

好的,所以最后我使用了来自@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"
    );